/*
Copyright (C) 2005 David Green <green@couchpotato.net>

This file is part of I3J.

I3J 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 2 of the License, or
(at your option) any later version.

I3J 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 I3J; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

package com.aelfengard.i3;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

public class LPCUtils {

    public static LPCMixed lpcToJava(String lpc) {
        return parseObject(lpc.toCharArray(), new int[1]);
    }
    
    public static String javaToLPC(Object o) {
        if (o instanceof LPCMixed) {
            o = ((LPCMixed) o).asObject();
        }
        if (o instanceof List) {
            List list = (List) o;
            StringBuffer sb = new StringBuffer();
            sb.append("({");
            for (Object item : list) {
                sb.append(javaToLPC(item));
                sb.append(',');
            }
            sb.append("})");
            return sb.toString();
        }
        else if (o instanceof String) {
            char[] s = ((String) o).toCharArray();
            StringBuffer sb = new StringBuffer();
            sb.append("\"");
            for (char c : s) {
                switch (c) {
                    case '\\': sb.append("\\\\"); break;
                    case '"': sb.append("\\\""); break;
                    default: sb.append(c); break;
                }
            }
            sb.append("\"");
            return sb.toString();
        }
        else if (o instanceof Integer) {
            return o.toString();
        }
        else if (o instanceof Map) {
            Map map = (Map) o;
            StringBuffer sb = new StringBuffer();
            sb.append("([");
            Iterator iter = map.entrySet().iterator();
            while (iter.hasNext()) {
                Map.Entry entry = (Map.Entry) iter.next();
                sb.append(javaToLPC(entry.getKey()));
                sb.append(':');
                sb.append(javaToLPC(entry.getValue()));
                sb.append(',');
            }
            sb.append("])");
            return sb.toString();
        }
        else if (o == null) {
            return "0";
        }
        else throw new IllegalArgumentException(o.getClass().getName());
    }

    private static LPCMixed parseObject(char[] packet, int[] idx) {
        switch (packet[idx[0]]) {
            case '\"': return new LPCMixed(parseString(packet, idx));
            case '(': {
                switch (packet[++idx[0]]) {
                    case '{': return new LPCMixed(parseList(packet, idx));
                    case '[': return new LPCMixed(parseMap(packet, idx));
                    default: throw new RuntimeException("Invalid struct type: " + packet[idx[0]]);
                }
            }
            default: return new LPCMixed(parseInt(packet, idx));
        }
    }
    
    private static List<LPCMixed> parseList(char[] packet, int[] idx) {
        idx[0]++;
        ArrayList<LPCMixed> list = new ArrayList<LPCMixed>();
        while (packet[idx[0]] != '}') {
            list.add(parseObject(packet, idx));
            idx[0]++; // skip comma
        }
        idx[0] += 2; // skip })
        return list;
    }
    
    private static Map<LPCMixed,LPCMixed> parseMap(char[] packet, int[] idx) {
        idx[0]++;
        Map<LPCMixed,LPCMixed> map = new LinkedHashMap<LPCMixed,LPCMixed>();
        while (packet[idx[0]] != ']') {
            LPCMixed key = parseObject(packet, idx);
            idx[0]++; // skip colon
            LPCMixed value = parseObject(packet, idx);
            idx[0]++; // skip comma
            map.put(key, value);
        }
        idx[0] += 2; // skip ])
        return map;
    }

    private static int parseInt(char[] packet, int[] idx) {
        int negative = 1;
        int i = 0;
        while (true) {
            if (packet[idx[0]] >= '0' && packet[idx[0]] <= '9') {
                i *= 10;
                i += packet[idx[0]] - '0';
            }
            else if (packet[idx[0]] == '-') {
                negative = -1;
            }
            else return negative * i;
            idx[0]++;
        }
    }
    
    private static String parseString(char[] packet, int[] idx) {
        StringBuffer sb = new StringBuffer();
        while (true) {
            idx[0]++;
            if (packet[idx[0]] == '"') {
                // end of string
                idx[0]++;
                return sb.toString();
            }
            if (packet[idx[0]] == '\\') {
                // skip backslash, but take next character verbatim
                idx[0]++;
            }
            sb.append(packet[idx[0]]);
        }
    }
    
    public static List<LPCMixed> makeList(Object... parts) {
        List<LPCMixed> list = new ArrayList<LPCMixed>(parts.length);
        for (Object part : parts) {
            list.add(part instanceof LPCMixed ? (LPCMixed) part : new LPCMixed(part));
        }
        return list;
    }
    
}