/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: NetlistImpl.java
 * Written by: Dmitry Nadezhin, Sun Microsystems.
 *
 * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.sun.electric.database.network;

import com.sun.electric.database.CellTree;
import com.sun.electric.database.Snapshot;
import com.sun.electric.database.text.Name;
import com.sun.electric.util.TextUtils;
import com.sun.electric.util.collections.ArrayIterator;

import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collection;
import java.util.Iterator;

/**
 *
 * @author dn146861
 */
public class NetlistImpl extends Netlist {

    private static final String[] NULL_STRING_ARRAY = {};
    /**
     * Arrays of names for each net.
     * First names are exported names in STRING_NUMBER_ORDER,
     * Then internal user-defined names in STRING_NUMBER_ORDER.
     * If this net has no exported or user-defined names, then this
     * list contains one of temporary names.
     * Hence the first name in the list are most appropriate.
     **/
    private String[][] names;
    /** Nets which have has user-defined names */
    private BitSet isUsernamed = new BitSet();
    /** Number of export names for each net */
    private int[] exportedNamesCount;
    private int[] equivPortIndexByNetIndex;

    NetlistImpl(NetCell netCell, int numExternals, int[] map) {
        super(netCell, Netlist.ShortResistors.NO, numExternals, map);
        if (netCell instanceof NetSchem) {
            expectedSnapshot = new WeakReference<Snapshot>(netCell.database.backup());
        } else {
            expectedCellTree = new WeakReference<CellTree>(netCell.cell.tree());
        }
        exportedNamesCount = new int[numExternalNets];
        equivPortIndexByNetIndex = new int[numExternalNets];
        Arrays.fill(equivPortIndexByNetIndex, -1);
        names = new String[getNumNetworks()][];
        Arrays.fill(names, NULL_STRING_ARRAY);
    }

    void setEquivPortIndexByNetIndex(int equivIndex, int netIndex) {
        equivPortIndexByNetIndex[netIndex] = equivIndex;
    }

    @Override
    Iterator<String> getNames(int netIndex) {
        return ArrayIterator.iterator(names[netIndex]);
    }

    @Override
    Iterator<String> getExportedNames(int netIndex) {
        int exportedNamesCount = netIndex < numExternalNets ? this.exportedNamesCount[netIndex] : 0;
        return ArrayIterator.iterator(names[netIndex], 0, exportedNamesCount);
    }

    @Override
    String getName(int netIndex) {
        return names[netIndex][0];
    }

    @Override
    boolean hasName(int netIndex, String nm) {
        String[] theseNames = names[netIndex];
        for (int i = 0; i < theseNames.length; i++) {
            if (theseNames[i].equals(nm)) {
                return true;
            }
        }
        return false;
    }

    @Override
    void fillNames(int netIndex, Collection<String> exportedNames, Collection<String> privateNames) {
        if (!isUsernamed(netIndex)) {
            return;
        }
        String[] names = this.names[netIndex];
        int exportedNamesCount = netIndex < numExternalNets ? this.exportedNamesCount[netIndex] : 0;
        for (int i = 0; i < exportedNamesCount; i++) {
            exportedNames.add(names[i]);
        }
        if (privateNames != null) {
            for (int i = exportedNamesCount; i < names.length; i++) {
                privateNames.add(names[i]);
            }
        }
    }

    @Override
    boolean isUsernamed(int netIndex) {
        return isUsernamed.get(netIndex);
    }

    boolean hasNames(int netIndex) {
        return names[netIndex].length > 0;
    }

    @Override
    int getEquivPortIndexByNetIndex(int netIndex) {
        return equivPortIndexByNetIndex[netIndex];
    }

    /**
     * Add user name to list of names of this Network.
     * @param netIndex index of Network
     * @param nameKey name key to add.
     * @param exported true if name is exported.
     */
    void addUserName(int netIndex, Name nameKey, boolean exported) {
        assert !nameKey.isTempname();
        String name = nameKey.toString();
        String[] theseNames = names[netIndex];
        int exportedCount = netIndex < numExternalNets ? exportedNamesCount[netIndex] : 0;
        if (exported) {
            assert exportedCount == theseNames.length;
        }
        int i = 0;
        for (; i < theseNames.length; i++) {
            String n = names[netIndex][i];
            int cmp = TextUtils.STRING_NUMBER_ORDER.compare(name, n);
            if (cmp == 0) {
                return;
            }
            if (cmp < 0 && (exported || i >= exportedCount)) {
                break;
            }
        }
        if (theseNames.length == 0) {
            names[netIndex] = new String[]{name};
        } else {
            String[] newNames = new String[theseNames.length + 1];
            System.arraycopy(theseNames, 0, newNames, 0, i);
            newNames[i] = name;
            System.arraycopy(theseNames, i, newNames, i + 1, theseNames.length - i);
            names[netIndex] = newNames;
        }
        if (exported) {
            exportedNamesCount[netIndex]++;
        }
        isUsernamed.set(netIndex);
    }

    void addTempName(int netIndex, String name) {
        assert names[netIndex].length == 0;
        names[netIndex] = new String[]{name};
    }
}
