package org.artisanlogiciel.xpm;

import java.awt.*;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Scanner;

public class Xpm {

    int width;
    int height;

    Color palette[] = null;

    HashMap<String,Integer> charColorIndex = null;

    // reversed y,x ( since xpm is line based ).
    short pixels[][] = null;

    int colors;

    // current filling of pixels[cy][cx]
    int cx=0;
    int cy=0;

    // number of char for color name
    int chars;


    DataInputStream input = null;
    Scanner scanner = null;

    public int getWidth()
    {
        return width;
    }

    public int getHeight()
    {
        return height;
    }

    public Color getColor(int x, int y)
    {
        if ( ( x >= 0 ) && ( x < width )  && ( y >= 0 ) && ( y < height )) {
            return palette[pixels[y][x]];
        }
        else
        {
            return palette[pixels[0][0]];
        }
    }

    public Color parseColor(String pColor)
    {
        if ( pColor.substring(0,1).equals("#"))
        {
            String r = pColor.substring(1,3);
            String g = pColor.substring( 3,5);
            String b = pColor.substring( 5, 7);

            return new Color(Integer.parseInt(r,16),Integer.parseInt(g, 16),Integer.parseInt(b, 16));
        }
        return null;
    }

    public Color parseColorLine(String pLine, int color)
    {
        // "chars<tab>c<space>#RhexGhexBhex"
        int firsttab = pLine.indexOf('\t');
        String charindex = pLine.substring(0,firsttab);
        charColorIndex.put(charindex, new Integer(color));
        int space = pLine.lastIndexOf(' ');
        return parseColor(pLine.substring(space + 1));
    }

    public boolean parseHeader(String pLine)
    {
        // "309 295 674 2"
        String words[] = pLine.split(" ");
        width = Integer.parseInt(words[0]);
        height = Integer.parseInt(words[1]);
        colors = Integer.parseInt(words[2]);
        chars = Integer.parseInt(words[3]);
        pixels = new short[height][width];
        charColorIndex = new HashMap<>(colors);
        palette = new Color[colors];

        return true;
    }

    int getIndexFromString(String pString)
    {
        Integer i = charColorIndex.get(pString);
        return i.intValue();
    }

    Color getColorFromString(String pString)
    {
        return palette[getIndexFromString(pString)];
    }

    void parseLine(String pLine)
    {
        for(int i = 0; i < pLine.length(); i+=2 ) {
            CharSequence seq = pLine.subSequence(i, i+chars);
            pixels[cy][cx++]= (short) getIndexFromString(seq.toString());
            if ( cx >= width )
            {
                cx = 0;
                cy++;
            }
        }
    }

    // get only lines containing full strings
    String readLine()
    {
        try {
            String line = scanner.nextLine();
            while (line != null) {
                int first_space = line.indexOf('"');
                int last_space = line.lastIndexOf('"');
                if ((first_space >= 0) && (last_space > first_space + 1)) {
                    String stripped = line.substring(first_space + 1, last_space);
                    // System.out.println(stripped);
                    return stripped;
                }
                System.out.println("!!" + line + "!!");
                line = scanner.nextLine();
            }
        }
        catch(Exception e)
        {
            System.err.println("!");
        }

        return null;

    }

    public boolean parse(InputStream stream)
    {
        scanner = new Scanner(stream);

        String line = readLine();
        if (line != null)
        {

            parseHeader(line);
            for (int l=0; l < colors; l++) {
                line = readLine();
                if (line != null) {
                    palette[l] = parseColorLine(line,l);
                } else {
                    scanner = null;
                    return false;
                }
            }

            for (int l=0; l< height; l++) {
                line = readLine();
                if (line != null )
                {
                    parseLine(line);
                }
                else
                {
                    scanner = null;
                    return false;
                }
            }

            scanner = null;
            return true;
        }

        scanner = null;
        return false;
    }

    public String toString()
    {
        StringBuffer b = new StringBuffer();
        for (int l = 0 ; l < palette.length; l++)
        {
            b.append( palette[l].toString());
        }
        return b.toString();
    }

    public static void main(String pArgs[])
    {
        if (pArgs.length > 0)
        {
            try {
                String filename = pArgs[0];
                System.out.println("PArsing xpm " + filename);
                FileInputStream f = new FileInputStream(filename);
                Xpm xpm = new Xpm();
                if ( xpm.parse(f) )
                {
                    System.out.println(xpm.toString());
                }
                else
                {
                    System.err.println("Fail to parse " + filename);
                }
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
    }
}
