package com.alibaba.json.test.performance;

import static com.alibaba.json.test.performance.JacksonPageModelParser.JsonParserHelper.assertExpectedFiled;
import static com.alibaba.json.test.performance.JacksonPageModelParser.JsonParserHelper.assertExpectedJsonToken;
import static com.alibaba.json.test.performance.JacksonPageModelParser.JsonParserHelper.getNextTextValue;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonLocation;
import org.codehaus.jackson.JsonParseException;
import org.codehaus.jackson.JsonParser;
import org.codehaus.jackson.JsonToken;

import com.alibaba.json.test.entity.pagemodel.LayoutInstance;
import com.alibaba.json.test.entity.pagemodel.PageInstance;
import com.alibaba.json.test.entity.pagemodel.RegionEnum;
import com.alibaba.json.test.entity.pagemodel.RegionInstance;
import com.alibaba.json.test.entity.pagemodel.SegmentInstance;
import com.alibaba.json.test.entity.pagemodel.WidgetInstance;

public class JacksonPageModelParser {

    /**
     * @param content
     * @throws JsonParseException
     * @throws IOException
     */
    public PageInstance parse(String content) throws JsonParseException, IOException {
        JsonFactory f = new JsonFactory();
        JsonParser parser = f.createJsonParser(content);

        JsonToken current = parser.nextToken(); // move to the start of the
        // object

        String instanceId = getNextTextValue("sid", parser); // get instanceId

        String pageId = getNextTextValue("cid", parser); // get pageId

        current = parser.nextToken(); // move to field: segments
        assertExpectedFiled(parser.getCurrentName(), "segments", parser.getCurrentLocation());

        PageInstance pageInstance = new PageInstance();
        pageInstance.setCid(pageId);
        pageInstance.setSid(Long.valueOf(instanceId));
        pageInstance.setSegments(parseSegments(parser));
        return pageInstance;
        // 构建组件树，用于递归渲染
        // pageInstance.buildComponentTree();

    }

    /**
     * @param parser
     * @throws JsonParseException
     * @throws IOException
     */
    private List<SegmentInstance> parseSegments(JsonParser parser) throws JsonParseException, IOException {
        JsonToken current = parser.nextToken();

        assertExpectedJsonToken(current, JsonToken.START_ARRAY, parser.getCurrentLocation());
        List<SegmentInstance> instances = new ArrayList<SegmentInstance>();
        while ((current = parser.nextToken()) != JsonToken.END_ARRAY) {

            assertExpectedJsonToken(current, JsonToken.START_OBJECT, parser.getCurrentLocation());

            String segmentId = getNextTextValue("cid", parser); // get pageId

            current = parser.nextToken(); // move to field: layouts
            assertExpectedFiled(parser.getCurrentName(), "layouts", parser.getCurrentLocation());
            SegmentInstance instance = new SegmentInstance();
            instance.setLayouts(parseLayouts(parser, segmentId));
            instances.add(instance);

            assertExpectedJsonToken((current = parser.nextToken()), JsonToken.END_OBJECT, parser.getCurrentLocation());

        }
        return instances;

    }

    /**
     * @param parser
     * @param segmentId
     * @throws JsonParseException
     * @throws IOException
     */
    private List<LayoutInstance> parseLayouts(JsonParser parser, String segment) throws JsonParseException, IOException {

        JsonToken current = parser.nextToken();

        assertExpectedJsonToken(current, JsonToken.START_ARRAY, parser.getCurrentLocation());

        List<LayoutInstance> layoutInThisSegment = new ArrayList<LayoutInstance>();

        while ((current = parser.nextToken()) != JsonToken.END_ARRAY) {

            assertExpectedJsonToken(current, JsonToken.START_OBJECT, parser.getCurrentLocation());

            String instanceId = getNextTextValue("sid", parser); // get
                                                                 // instanceId
            String layoutId = getNextTextValue("cid", parser); // get
                                                               // layoutId

            LayoutInstance layoutInstance = new LayoutInstance();
            layoutInstance.setCid(layoutId);
            layoutInstance.setSid(Long.valueOf(instanceId));
            layoutInThisSegment.add(layoutInstance);

            current = parser.nextToken(); // move to field: regions
            assertExpectedFiled(parser.getCurrentName(), "regions", parser.getCurrentLocation());

            layoutInstance.setRegions(parseRegions(parser, segment));

            assertExpectedJsonToken((current = parser.nextToken()), JsonToken.END_OBJECT, parser.getCurrentLocation());
        }
        return layoutInThisSegment;

    }

    /**
     * @param parser
     * @param layoutId
     * @throws JsonParseException
     * @throws IOException
     */
    private List<RegionInstance> parseRegions(JsonParser parser, String segment) throws JsonParseException, IOException {

        JsonToken current = parser.nextToken();

        assertExpectedJsonToken(current, JsonToken.START_ARRAY, parser.getCurrentLocation());
        List<RegionInstance> instances = new ArrayList<RegionInstance>();
        while ((current = parser.nextToken()) != JsonToken.END_ARRAY) {

            assertExpectedJsonToken(current, JsonToken.START_OBJECT, parser.getCurrentLocation());

            String regionId = getNextTextValue("cid", parser); // get regionId
            RegionEnum region = RegionEnum.valueOf(regionId);

            current = parser.nextToken(); // move to field: widgtes
            assertExpectedFiled(parser.getCurrentName(), "widgets", parser.getCurrentLocation());
            RegionInstance instance = new RegionInstance();
            instance.setWidgtes(parseWidgets(parser, region));
            instances.add(instance);

            assertExpectedJsonToken((current = parser.nextToken()), JsonToken.END_OBJECT, parser.getCurrentLocation());
        }
        return instances;

    }

    /**
     * @param parser
     * @param layoutId
     * @param regionId
     * @throws JsonParseException
     * @throws IOException
     */
    private List<WidgetInstance> parseWidgets(JsonParser parser, RegionEnum region) throws JsonParseException, IOException {

        JsonToken current = parser.nextToken();

        assertExpectedJsonToken(current, JsonToken.START_ARRAY, parser.getCurrentLocation());

        List<WidgetInstance> widgetInThisRegion = new ArrayList<WidgetInstance>();

        while ((current = parser.nextToken()) != JsonToken.END_ARRAY) {

            assertExpectedJsonToken(current, JsonToken.START_OBJECT, parser.getCurrentLocation());

            String instanceId = getNextTextValue("sid", parser); // get
                                                                 // instanceId
            String widgetId = getNextTextValue("cid", parser); // get
                                                               // widgetId

            WidgetInstance widgetInstance = new WidgetInstance();
            widgetInstance.setCid(widgetId);
            widgetInstance.setSid(Long.valueOf(instanceId));

            widgetInThisRegion.add(widgetInstance);

            assertExpectedJsonToken((current = parser.nextToken()), JsonToken.END_OBJECT, parser.getCurrentLocation());
        }
        return widgetInThisRegion;

    }

    public static class JsonParserHelper {

        public static String getNextTextValue(String fieldName, JsonParser parser) throws JsonParseException, IOException {

            JsonToken current = parser.nextToken(); // move to filed
            if (current != JsonToken.FIELD_NAME || !fieldName.equals(parser.getCurrentName())) {
                reportParseError("Error occoured while getting value by field name:" + fieldName, parser.getCurrentLocation());
            }
            current = parser.nextToken(); // move to value
            return parser.getText();

        }

        public static void assertExpectedJsonToken(JsonToken object, JsonToken expected, JsonLocation jsonLoc) throws JsonParseException {
            if (object != expected) {
                reportParseError(buildFailMessage(object, expected), jsonLoc);
            }
        }

        public static void assertExpectedFiled(String object, String expected, JsonLocation jsonLoc) throws JsonParseException {
            if (!expected.equals(object)) {
                reportParseError(buildFailMessage(object, expected), jsonLoc);
            }
        }

        private static String buildFailMessage(Object object, Object expected) {
            StringBuilder sb = new StringBuilder();
            sb.append("get [").append(object).append("] but expect [").append(expected).append("] !");
            return sb.toString();
        }

        public static void reportParseError(String errorMsg, JsonLocation jsonLoc) throws JsonParseException {

            throw new JsonParseException(errorMsg, jsonLoc);

        }

    }
}
