// Copyright (c) Corporation for National Research Initiatives
package org.python.compiler;

import java.util.*;
import java.io.*;


class Bytes
{
    public byte[] data;

    Bytes(ByteArrayOutputStream data) {
        this.data = data.toByteArray();
    }

    public boolean equals(Object o) {
        if (o instanceof Bytes) {
            byte[] odata = ((Bytes)o).data;
            int n = data.length;
            if (odata.length != n)
                return false;
            for (int i=0; i<n; i++) {
                if (data[i] != odata[i])
                    return false;
            }
            return true;
        }
        return false;
    }

    public int hashCode() {
        int h = 0xa538;
        int n = data.length;
        for (int i=0; i<n; i++)
            h = h ^ data[i];
        return h;
    }
}



public class ConstantPool
{
    Hashtable constants;
    int index;
    DataOutputStream tdata;
    ByteArrayOutputStream pool, tarray;
    int[] sizes;

    public ConstantPool() {
        constants = new Hashtable();
        index = 0;
        pool = new ByteArrayOutputStream();
        tarray = new ByteArrayOutputStream();
        tdata = new DataOutputStream(tarray);
        sizes = new int[256];
    }

    public void write(DataOutputStream stream) throws IOException {
        stream.writeShort(index+1);
        stream.write(pool.toByteArray());
    }

    public int addConstant(int slots) throws IOException {
        //tarray.flush();
        //byte[] data = tarray.toByteArray();
        Bytes data = new Bytes(tarray);
        tarray.reset();
        Integer i = (Integer)constants.get(data);
        if (i == null) {
            pool.write(data.data);
            i = new Integer(index);
            constants.put(data, i);
            if (index+1 >= sizes.length) {
                int[] new_sizes = new int[sizes.length*2];
                System.arraycopy(sizes, 0, new_sizes, 0, sizes.length);
                sizes = new_sizes;
            }
            sizes[index+1] = slots;
            index += slots;
        }
        //System.out.print("Constant: ");
        //for(int j=0; j<data.length; j++)
        //      System.out.print(Integer.toString(data[j])+", ");
        //System.out.println("");
        return i.intValue()+1;
    }

    public int UTF8(String s) throws IOException {
        tdata.writeByte(1);
        tdata.writeUTF(s);
        return addConstant(1);
    }

    public int Class(String s) throws IOException {
        int c = UTF8(s);
        tdata.writeByte(7);
        tdata.writeShort(c);
        return addConstant(1);
    }

    public int Fieldref(String c, String name, String type)
        throws IOException
    {
        int ic = Class(c);
        int nt = NameAndType(name, type);
        tdata.writeByte(9);
        tdata.writeShort(ic);
        tdata.writeShort(nt);
        int size = 1;
        if (type.equals("D") || type.equals("J"))
            size = 2;
        int index = addConstant(1);
        sizes[index] = size;
        //System.out.println("field: "+c+", "+name+", "+type+": "+index);
        return index;
    }

    public static int sigSize(String sig, boolean includeReturn) {
        int stack = 0;
        int i = 0;
        char[] c = sig.toCharArray();
        int n = c.length;
        boolean ret=false;
        boolean array=false;

        while (++i<n) {
            switch (c[i]) {
            case ')':
                if (!includeReturn)
                    return stack;
                ret=true;
                continue;
            case '[':
                array=true;
                continue;
            case 'V':
                continue;
            case 'D':
            case 'J':
                if (array) {
                    if (ret) stack += 1;
                    else stack -=1;
                    array = false;
                } else {
                    if (ret) stack += 2;
                    else stack -=2;
                }
                break;
            case 'L':
                while (c[++i] != ';') {;}
            default:
                if (ret) stack++;
                else stack--;
                array = false;
            }
        }
        return stack;
    }

    public int Methodref(String c, String name, String type)
        throws IOException
    {
        int ic = Class(c);
        int nt = NameAndType(name, type);

        tdata.writeByte(10);
        tdata.writeShort(ic);
        tdata.writeShort(nt);
        int index = addConstant(1);
        sizes[index] = sigSize(type, true);
        //System.out.println("method: "+c+", "+name+", "+type+": "+index);
        return index;
    }

    public int InterfaceMethodref(String c, String name, String type)
        throws IOException
    {
        int ic = Class(c);
        int nt = NameAndType(name, type);

        tdata.writeByte(11);
        tdata.writeShort(ic);
        tdata.writeShort(nt);
        int index = addConstant(1);
        sizes[index] = sigSize(type, true);
        return index;
    }

    public int String(String s) throws IOException {
        int i = UTF8(s);
        tdata.writeByte(8);
        tdata.writeShort(i);
        return addConstant(1);
    }

    public int Integer(int i) throws IOException {
        tdata.writeByte(3);
        tdata.writeInt(i);
        return addConstant(1);
    }

    public int Float(float f) throws IOException {
        tdata.writeByte(4);
        tdata.writeFloat(f);
        return addConstant(1);
    }

    public int Long(long l) throws IOException {
        tdata.writeByte(5);
        tdata.writeLong(l);
        return addConstant(2);
    }

    public int Double(double d) throws IOException {
        tdata.writeByte(6);
        tdata.writeDouble(d);
        return addConstant(2);
    }

    public int NameAndType(String name, String type) throws IOException {
        int n = UTF8(name);
        int t = UTF8(type);

        tdata.writeByte(12);
        tdata.writeShort(n);
        tdata.writeShort(t);
        return addConstant(1);
    }

    public static void main(String[] args) throws Exception {
        ConstantPool cp = new ConstantPool();

        System.out.println("c: "+cp.Class("org/python/core/PyString"));
        System.out.println("c: "+cp.Class("org/python/core/PyString"));

        for (int i=0; i<args.length; i++)
            System.out.println(args[i]+": "+sigSize(args[i], true));
    }
}
