/* -*- tab-width: 4 -*-
 *
 * Electric(tm) VLSI Design System
 *
 * File: IdManager.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.id;

import com.sun.electric.database.Environment;
import com.sun.electric.database.Snapshot;
import com.sun.electric.database.text.CellName;
import com.sun.electric.technology.TechPool;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * This class owns a set of LibIds and CellIds.
 */
public class IdManager {

    /** Standard IdManager */
    public static final IdManager stdIdManager = new IdManager();
    /** List of TechIds created so far. */
    final ArrayList<TechId> techIds = new ArrayList<TechId>();
    /** HashMap of TechIds by their tech name. */
    private final HashMap<String, TechId> techIdsByName = new HashMap<String, TechId>();
    /** List of LibIds created so far. */
    final ArrayList<LibId> libIds = new ArrayList<LibId>();
    /** HashMap of LibIds by their lib name. */
    private final HashMap<String, LibId> libIdsByName = new HashMap<String, LibId>();
    /** List of CellIds created so far. */
    final ArrayList<CellId> cellIds = new ArrayList<CellId>();
    /** Count of Snapshots created with this IdManager. */
    private final AtomicInteger snapshotCount = new AtomicInteger();
    /** Initial TechPool. */
    private final TechPool initialTechPool = new TechPool(this);
    /** Initial Environment. */
    private final Environment initialEnvironment = new Environment(this);
    /** Initial Snapshot. */
    private final Snapshot initialSnapshot = new Snapshot(this);
    volatile boolean readOnly;

    /** Creates a new instance of IdManager */
    public IdManager() {
    }

    /** Disallow creation of ids (except IdReader */
    public void setReadOnly() {
        readOnly = true;
    }

    /**
     * Returns TechId with specified techName.
     * @param techName technology name.
     * @return TechId with specified techName.
     */
    public synchronized TechId newTechId(String techName) {
        TechId techId = techIdsByName.get(techName);
        if (techId != null) {
            return techId;
        }
        assert !readOnly;
        return newTechIdInternal(techName);
    }

    /**
     * Returns TechId by given index.
     * @param techIndex given index.
     * @return TechId with given index.
     */
    public synchronized TechId getTechId(int techIndex) {
        return techIds.get(techIndex);
    }

    TechId newTechIdInternal(String techName) {
        int techIndex = techIds.size();
        TechId techId = new TechId(this, techName, techIndex);
        techIds.add(techId);
        techIdsByName.put(techName, techId);
        assert techIds.size() == techIdsByName.size();
        return techId;
    }

    /**
     * Returns LibId with specified libName.
     * @param libName library name.
     * @return LibId with specified libName.
     */
    public synchronized LibId newLibId(String libName) {
        assert !readOnly;
        LibId libId = libIdsByName.get(libName);
        return libId != null ? libId : newLibIdInternal(libName);
    }

    /**
     * Returns LibId by given index.
     * @param libIndex given index.
     * @return LibId with given index.
     */
    public synchronized LibId getLibId(int libIndex) {
        return libIds.get(libIndex);
    }

    LibId newLibIdInternal(String libName) {
        int libIndex = libIds.size();
        LibId libId = new LibId(this, libName, libIndex);
        libIds.add(libId);
        libIdsByName.put(libName, libId);
        assert libIds.size() == libIdsByName.size();
        return libId;
    }

    /**
     * Returns new CellId with cellIndex unique in this IdManager.
     * @param libId library to which the Cell belongs
     * @param cellName name of the Cell.
     * @return new CellId.
     */
    synchronized CellId newCellId(LibId libId, CellName cellName) {
        assert !readOnly;
        assert libId.idManager == this;
        CellId cellId = libId.getCellId(cellName);
        return cellId != null ? cellId : newCellIdInternal(libId, cellName);
    }

    /**
     * Returns CellId by given index.
     * @param cellIndex given index.
     * @return CellId with given index.
     */
    public synchronized CellId getCellId(int cellIndex) {
        return cellIds.get(cellIndex);
    }

    CellId newCellIdInternal(LibId libId, CellName cellName) {
        int cellIndex = cellIds.size();
        CellId cellId = new CellId(libId, cellName, cellIndex);
        cellIds.add(cellId);
        libId.putCellId(cellId);
        return cellId;
    }

    public TechPool getInitialTechPool() {
        return initialTechPool;
    }

    public Environment getInitialEnvironment() {
        return initialEnvironment;
    }

    public Snapshot getInitialSnapshot() {
        return initialSnapshot;
    }

    public int newSnapshotId() {
        return snapshotCount.incrementAndGet();
    }

    /**
     * Method to check invariants in all Libraries.
     */
    public void checkInvariants() {
        int numTechIds;
        int numLibIds;
        int numCellIds;
        synchronized (this) {
            numTechIds = techIds.size();
            assert numTechIds == techIdsByName.size();
            for (int techIndex = 0; techIndex < numTechIds; techIndex++) {
                TechId techId = getTechId(techIndex);
                assert techId.idManager == this;
                assert techId.techIndex == techIndex;
                techId.check();
                assert techIdsByName.get(techId.techName) == techId;
            }
            numLibIds = libIds.size();
            assert numLibIds == libIdsByName.size();
            for (int libIndex = 0; libIndex < numLibIds; libIndex++) {
                LibId libId = getLibId(libIndex);
                assert libId.idManager == this;
                assert libId.libIndex == libIndex;
                libId.check();
                assert libIdsByName.get(libId.libName) == libId;
            }
            for (Map.Entry<String, LibId> e : libIdsByName.entrySet()) {
                LibId libId = e.getValue();
                assert libId.idManager == this;
                assert libId.libName == e.getKey();
                assert getLibId(libId.libIndex) == libId;
            }

            numCellIds = cellIds.size();
        }
        for (int cellIndex = 0; cellIndex < numCellIds; cellIndex++) {
            CellId cellId = getCellId(cellIndex);
            assert cellId.idManager == this;
            assert cellId.cellIndex == cellIndex;
            assert cellId.libId.getCellId(cellId.cellName) == cellId;
            cellId.check();
        }
    }

    public void dump() {
        System.out.println(techIds.size() + " TechIds:");
        for (TechId techId : new TreeMap<String, TechId>(techIdsByName).values()) {
            System.out.println("TechId " + techId + " " + techId.primitiveNodeIds.size() + " " + techId.arcProtoIds.size());
        }
        System.out.println(libIds.size() + " LibIds:");
        for (LibId libId : new TreeMap<String, LibId>(libIdsByName).values()) {
            System.out.println("LibId " + libId);
            for (CellId cellId : cellIds) {
                if (cellId.libId != libId) {
                    continue;
                }
                System.out.println(cellId + " " + cellId.numUsagesIn() + " " + cellId.numUsagesOf() + " " + cellId.numExportIds());
            }
        }
    }
}
