package org.sunflow.core.parser;

import java.io.IOException;

import org.sunflow.core.ParameterList.InterpolationType;
import org.sunflow.image.Color;
import org.sunflow.math.Matrix4;
import org.sunflow.system.Parser;
import org.sunflow.system.UI;
import org.sunflow.system.Parser.ParserException;
import org.sunflow.system.UI.Module;

public class SCAsciiParser extends SCAbstractParser {
    private Parser p;

    protected Color parseColor() throws IOException {
        String space = p.getNextToken();
        Color c = null;
        if (space.equals("sRGB nonlinear")) {
            float r = p.getNextFloat();
            float g = p.getNextFloat();
            float b = p.getNextFloat();
            c = new Color(r, g, b);
            c.toLinear();
        } else if (space.equals("sRGB linear")) {
            float r = p.getNextFloat();
            float g = p.getNextFloat();
            float b = p.getNextFloat();
            c = new Color(r, g, b);
        } else
            UI.printWarning(Module.API, "Unrecognized color space: %s", space);
        return c;
    }

    @Override
    protected Matrix4 parseMatrix() throws IOException {
        if (p.peekNextToken("row")) {
            return new Matrix4(parseFloatArray(16), true);
        } else if (p.peekNextToken("col")) {
            return new Matrix4(parseFloatArray(16), false);
        } else {
            Matrix4 m = Matrix4.IDENTITY;
            try {
                p.checkNextToken("{");
            } catch (ParserException e) {
                throw new IOException(e.getMessage());
            }
            while (!p.peekNextToken("}")) {
                Matrix4 t = null;
                if (p.peekNextToken("translate")) {
                    float x = p.getNextFloat();
                    float y = p.getNextFloat();
                    float z = p.getNextFloat();
                    t = Matrix4.translation(x, y, z);
                } else if (p.peekNextToken("scaleu")) {
                    float s = p.getNextFloat();
                    t = Matrix4.scale(s);
                } else if (p.peekNextToken("scale")) {
                    float x = p.getNextFloat();
                    float y = p.getNextFloat();
                    float z = p.getNextFloat();
                    t = Matrix4.scale(x, y, z);
                } else if (p.peekNextToken("rotatex")) {
                    float angle = p.getNextFloat();
                    t = Matrix4.rotateX((float) Math.toRadians(angle));
                } else if (p.peekNextToken("rotatey")) {
                    float angle = p.getNextFloat();
                    t = Matrix4.rotateY((float) Math.toRadians(angle));
                } else if (p.peekNextToken("rotatez")) {
                    float angle = p.getNextFloat();
                    t = Matrix4.rotateZ((float) Math.toRadians(angle));
                } else if (p.peekNextToken("rotate")) {
                    float x = p.getNextFloat();
                    float y = p.getNextFloat();
                    float z = p.getNextFloat();
                    float angle = p.getNextFloat();
                    t = Matrix4.rotate(x, y, z, (float) Math.toRadians(angle));
                } else
                    UI.printWarning(Module.API, "Unrecognized transformation type: %s", p.getNextToken());
                if (t != null)
                    m = t.multiply(m);
            }
            return m;
        }
    }

    @Override
    protected void closeParser() throws IOException {
        p.close();
    }

    @Override
    protected void openParser(String filename) throws IOException {
        p = new Parser(filename);
    }

    @Override
    protected boolean parseBoolean() throws IOException {
        return Boolean.parseBoolean(parseString());
    }

    @Override
    protected float parseFloat() throws IOException {
        return p.getNextFloat();
    }

    @Override
    protected int parseInt() throws IOException {
        return p.getNextInt();
    }

    @Override
    protected String parseString() throws IOException {
        return p.getNextToken();
    }

    @Override
    protected String parseVerbatimString() throws IOException {
        try {
            return p.getNextCodeBlock();
        } catch (ParserException e) {
            throw new IOException(e.getMessage());
        }
    }

    @Override
    protected InterpolationType parseInterpolationType() throws IOException {
        if (p.peekNextToken("none"))
            return InterpolationType.NONE;
        else if (p.peekNextToken("vertex"))
            return InterpolationType.VERTEX;
        else if (p.peekNextToken("face"))
            return InterpolationType.FACE;
        else if (p.peekNextToken("facevarying"))
            return InterpolationType.FACEVARYING;
        return InterpolationType.NONE;
    }

    @Override
    protected Keyword parseKeyword() throws IOException {
        String keyword = p.getNextToken();
        if (keyword == null)
            return Keyword.END_OF_FILE;
        if (anyEqual(keyword, "reset"))
            return Keyword.RESET;
        if (anyEqual(keyword, "parameter", "param", "p"))
            return Keyword.PARAMETER;
        if (anyEqual(keyword, "geometry", "geom", "g"))
            return Keyword.GEOMETRY;
        if (anyEqual(keyword, "instance", "inst", "i"))
            return Keyword.INSTANCE;
        if (anyEqual(keyword, "shader", "shd", "s"))
            return Keyword.SHADER;
        if (anyEqual(keyword, "modifier", "mod", "m"))
            return Keyword.MODIFIER;
        if (anyEqual(keyword, "light", "l"))
            return Keyword.LIGHT;
        if (anyEqual(keyword, "camera", "cam", "c"))
            return Keyword.CAMERA;
        if (anyEqual(keyword, "options", "opt", "o"))
            return Keyword.OPTIONS;
        if (anyEqual(keyword, "include", "inc"))
            return Keyword.INCLUDE;
        if (anyEqual(keyword, "remove"))
            return Keyword.REMOVE;
        if (anyEqual(keyword, "frame"))
            return Keyword.FRAME;
        if (anyEqual(keyword, "plugin", "plug"))
            return Keyword.PLUGIN;
        if (anyEqual(keyword, "searchpath"))
            return Keyword.SEARCHPATH;
        if (anyEqual(keyword, "string", "str"))
            return Keyword.STRING;
        if (anyEqual(keyword, "string[]", "str[]"))
            return Keyword.STRING_ARRAY;
        if (anyEqual(keyword, "boolean", "bool"))
            return Keyword.BOOL;
        if (anyEqual(keyword, "integer", "int"))
            return Keyword.INT;
        if (anyEqual(keyword, "integer[]", "int[]"))
            return Keyword.INT_ARRAY;
        if (anyEqual(keyword, "float", "flt"))
            return Keyword.FLOAT;
        if (anyEqual(keyword, "float[]", "flt[]"))
            return Keyword.FLOAT_ARRAY;
        if (anyEqual(keyword, "color", "col"))
            return Keyword.COLOR;
        if (anyEqual(keyword, "point", "pnt"))
            return Keyword.POINT;
        if (anyEqual(keyword, "point[]", "pnt[]"))
            return Keyword.POINT_ARRAY;
        if (anyEqual(keyword, "vector", "vec"))
            return Keyword.VECTOR;
        if (anyEqual(keyword, "vector[]", "vec[]"))
            return Keyword.VECTOR_ARRAY;
        if (anyEqual(keyword, "texcoord", "tex"))
            return Keyword.TEXCOORD;
        if (anyEqual(keyword, "texcoord[]", "tex[]"))
            return Keyword.TEXCOORD_ARRAY;
        if (anyEqual(keyword, "matrix", "mat"))
            return Keyword.MATRIX;
        if (anyEqual(keyword, "matrix[]", "mat[]"))
            return Keyword.MATRIX_ARRAY;
        return null;
    }

    private boolean anyEqual(String source, String... values) {
        for (String v : values)
            if (source.equals(v))
                return true;
        return false;
    }
}