/**
 * Copyright (c) 2005-2013 by Appcelerator, Inc. All Rights Reserved.
 * Licensed under the terms of the Eclipse Public License (EPL).
 * Please see the license.txt included with this distribution for details.
 * Any modifications to this file must keep this entire header intact.
 */
package com.python.pydev.analysis.additionalinfo;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

import org.python.pydev.core.IPythonNature;
import org.python.pydev.core.ObjectsInternPool;
import org.python.pydev.shared_core.string.FastStringBuffer;
import org.python.pydev.shared_core.string.StringUtils;

/**
 * @author fabioz
 *
 */
public class InfoStrFactory {

    /**
     * @param iInfo
     * @return
     */
    public static String infoToString(List<IInfo> iInfo) {
        HashMap<String, Integer> map = new HashMap<String, Integer>();

        FastStringBuffer infos = new FastStringBuffer();

        int next = 0;
        map.put(null, next);
        next++;

        for (Iterator<IInfo> it = iInfo.iterator(); it.hasNext();) {
            IInfo info = it.next();
            infos.append("&&");
            infos.append(info.getType());
            String name = info.getName();
            String declaringModuleName = info.getDeclaringModuleName();
            String path = info.getPath();

            next = add(map, infos, next, name);
            next = add(map, infos, next, declaringModuleName);
            next = add(map, infos, next, path);
        }

        FastStringBuffer header = new FastStringBuffer("INFOS:", map.size() * 30);
        Set<Entry<String, Integer>> entrySet = map.entrySet();

        //null is always 0 (not written to header)
        header.append(infos);
        header.append('\n');
        map.remove(null);
        for (Entry<String, Integer> entry : entrySet) {
            header.append(entry.getKey());
            header.append("=");
            header.append(entry.getValue());
            header.append("\n");
        }

        return header.toString();
    }

    private static int add(HashMap<String, Integer> map, FastStringBuffer infos, int next, String d) {
        Integer v = map.get(d);
        if (v == null) {
            v = next;
            map.put(d, next);
            next++;
        }
        infos.append("|");
        infos.append(v);
        return next;
    }

    /**
     * @param s
     * 
     * Some string as:
     * 
     * INFOS:&&2|1|2|0&&1|3|4|0&&2|1|2|0
     * Class=3
     * ClassMod=4
     * Foo=2
     * Bar=1
     * 
     * where number 0 is always null and the others are the numbers mapped as needed.
     */
    public static List<IInfo> strToInfo(String s, IPythonNature nature) {
        if (!s.startsWith("INFOS:")) {
            return null;
        }
        s = s.substring(6);

        Iterable<String> iterLines = StringUtils.iterLines(s);
        Iterator<String> linesIt = iterLines.iterator();
        if (!linesIt.hasNext()) {
            return null;
        }
        String firstLine = linesIt.next().trim(); //line with the infos (we must read the other parts to actually 'get' it).

        HashMap<Integer, String> map = new HashMap<Integer, String>();
        map.put(0, null);
        synchronized (ObjectsInternPool.lock) {
            while (linesIt.hasNext()) {
                String line = linesIt.next().trim();
                int i = StringUtils.rFind(line, '=');
                if (i > 0) {
                    String token = line.substring(0, i);
                    String value = line.substring(i + 1);

                    map.put(Integer.parseInt(value), ObjectsInternPool.internUnsynched(token));
                }
            }
        }

        ArrayList<IInfo> ret = new ArrayList<IInfo>();

        List<String> split = StringUtils.split(firstLine, "&&");
        for (String string : split) {
            List<String> parts = StringUtils.split(string, '|');
            int type = Integer.parseInt(parts.get(0));
            int name = Integer.parseInt(parts.get(1));
            int declaringModuleName = Integer.parseInt(parts.get(2));
            int path = Integer.parseInt(parts.get(3));

            switch (type) {
                case AbstractInfo.NAME_WITH_IMPORT_TYPE:
                    //no intern construct (already interned when creating the map)
                    ret.add(new NameInfo(map.get(name), map.get(declaringModuleName), map.get(path), true, nature));
                    break;

                case AbstractInfo.ATTRIBUTE_WITH_IMPORT_TYPE:
                    //no intern construct (already interned when creating the map)
                    ret.add(new AttrInfo(map.get(name), map.get(declaringModuleName), map.get(path), true, nature));
                    break;

                case AbstractInfo.METHOD_WITH_IMPORT_TYPE:
                    //no intern construct (already interned when creating the map)
                    ret.add(new FuncInfo(map.get(name), map.get(declaringModuleName), map.get(path), true, nature));
                    break;

                case AbstractInfo.CLASS_WITH_IMPORT_TYPE:
                    //no intern construct (already interned when creating the map)
                    ret.add(new ClassInfo(map.get(name), map.get(declaringModuleName), map.get(path), true, nature));
                    break;

            }
        }
        return ret;
    }

}
