/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 *    contributor license agreements.  See the NOTICE file distributed with
 *    this work for additional information regarding copyright ownership.
 *    The ASF licenses this file to You under the Apache License, Version 2.0
 *    (the "License"); you may not use this file except in compliance with
 *    the License.  You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package org.apache.poi.hssf.usermodel;

import org.apache.poi.ddf.*;
import org.apache.poi.hssf.record.*;
import org.apache.poi.poifs.filesystem.DirectoryNode;

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

/**
 * Factory class for producing Excel Shapes from Escher records
 */
public class HSSFShapeFactory {

    private final static short       OBJECT_TYPE_LINE               = 1;
    private final static short       OBJECT_TYPE_RECTANGLE          = 2;
    private final static short       OBJECT_TYPE_OVAL               = 3;
    private final static short       OBJECT_TYPE_ARC                = 4;
    private final static short       OBJECT_TYPE_PICTURE            = 8;

    /**
     * build shape tree from escher container
     * @param container root escher container from which escher records must be taken
     * @param agg - EscherAggregate
     * @param out - shape container to which shapes must be added
     * @param root - node to create HSSFObjectData shapes
     */
    public static void createShapeTree(EscherContainerRecord container, EscherAggregate agg, HSSFShapeContainer out, DirectoryNode root) {
        if (container.getRecordId() == EscherContainerRecord.SPGR_CONTAINER) {
            ObjRecord obj = null;
            EscherClientDataRecord clientData = ((EscherContainerRecord) container.getChild(0)).getChildById(EscherClientDataRecord.RECORD_ID);
            if (null != clientData) {
                obj = (ObjRecord) agg.getShapeToObjMapping().get(clientData);
            }
            HSSFShapeGroup group = new HSSFShapeGroup(container, obj);
            List<EscherContainerRecord> children = container.getChildContainers();
            // skip the first child record, it is group descriptor
            for (int i = 0; i < children.size(); i++) {
                EscherContainerRecord spContainer = children.get(i);
                if (i != 0) {
                    createShapeTree(spContainer, agg, group, root);
                }
            }
            out.addShape(group);
        } else if (container.getRecordId() == EscherContainerRecord.SP_CONTAINER) {
            Map<EscherRecord, Record> shapeToObj = agg.getShapeToObjMapping();
            ObjRecord objRecord = null;
            TextObjectRecord txtRecord = null;

            for (EscherRecord record : container.getChildRecords()) {
                switch (record.getRecordId()) {
                    case EscherClientDataRecord.RECORD_ID:
                        objRecord = (ObjRecord) shapeToObj.get(record);
                        break;
                    case EscherTextboxRecord.RECORD_ID:
                        txtRecord = (TextObjectRecord) shapeToObj.get(record);
                        break;
                }
            }
            if (isEmbeddedObject(objRecord)) {
                HSSFObjectData objectData = new HSSFObjectData(container, objRecord, root);
                out.addShape(objectData);
                return;
            }
            CommonObjectDataSubRecord cmo = (CommonObjectDataSubRecord) objRecord.getSubRecords().get(0);
            HSSFShape shape;
            switch (cmo.getObjectType()) {
                case CommonObjectDataSubRecord.OBJECT_TYPE_PICTURE:
                    shape = new HSSFPicture(container, objRecord);
                    break;
                case CommonObjectDataSubRecord.OBJECT_TYPE_RECTANGLE:
                    shape = new HSSFSimpleShape(container, objRecord, txtRecord);
                    break;
                case CommonObjectDataSubRecord.OBJECT_TYPE_LINE:
                    shape = new HSSFSimpleShape(container, objRecord);
                    break;
                case CommonObjectDataSubRecord.OBJECT_TYPE_COMBO_BOX:
                    shape = new HSSFCombobox(container, objRecord);
                    break;
                case CommonObjectDataSubRecord.OBJECT_TYPE_MICROSOFT_OFFICE_DRAWING:
                    EscherOptRecord optRecord = container.getChildById(EscherOptRecord.RECORD_ID);
                    EscherProperty property = optRecord.lookup(EscherProperties.GEOMETRY__VERTICES);
                    if (null != property) {
                        shape = new HSSFPolygon(container, objRecord, txtRecord);
                    } else {
                        shape = new HSSFSimpleShape(container, objRecord, txtRecord);
                    }
                    break;
                case CommonObjectDataSubRecord.OBJECT_TYPE_TEXT:
                    shape = new HSSFTextbox(container, objRecord, txtRecord);
                    break;
                case CommonObjectDataSubRecord.OBJECT_TYPE_COMMENT:
                    shape = new HSSFComment(container, objRecord, txtRecord, agg.getNoteRecordByObj(objRecord));
                    break;
                default:
                    shape = new HSSFSimpleShape(container, objRecord, txtRecord);
            }
            out.addShape(shape);
        }
    }

    private static boolean isEmbeddedObject(ObjRecord obj) {
        Iterator<SubRecord> subRecordIter = obj.getSubRecords().iterator();
        while (subRecordIter.hasNext()) {
            SubRecord sub = subRecordIter.next();
            if (sub instanceof EmbeddedObjectRefSubRecord) {
                return true;
            }
        }
        return false;
    }
}
