/* ====================================================================
   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 static org.apache.poi.hssf.record.TextObjectRecord.HORIZONTAL_TEXT_ALIGNMENT_CENTERED;
import static org.apache.poi.hssf.record.TextObjectRecord.VERTICAL_TEXT_ALIGNMENT_CENTER;

import org.apache.poi.ddf.DefaultEscherRecordFactory;
import org.apache.poi.ddf.EscherBoolProperty;
import org.apache.poi.ddf.EscherClientDataRecord;
import org.apache.poi.ddf.EscherContainerRecord;
import org.apache.poi.ddf.EscherOptRecord;
import org.apache.poi.ddf.EscherPropertyTypes;
import org.apache.poi.ddf.EscherRGBProperty;
import org.apache.poi.ddf.EscherShapePathProperty;
import org.apache.poi.ddf.EscherSimpleProperty;
import org.apache.poi.ddf.EscherSpRecord;
import org.apache.poi.ddf.EscherTextboxRecord;
import org.apache.poi.hssf.record.CommonObjectDataSubRecord;
import org.apache.poi.hssf.record.EndSubRecord;
import org.apache.poi.hssf.record.EscherAggregate;
import org.apache.poi.hssf.record.ObjRecord;
import org.apache.poi.hssf.record.TextObjectRecord;
import org.apache.poi.ss.usermodel.RichTextString;
import org.apache.poi.ss.usermodel.SimpleShape;

/**
 * Represents a simple shape such as a line, rectangle or oval.
 */
public class HSSFSimpleShape extends HSSFShape implements SimpleShape
{
    // The commented out ones haven't been tested yet or aren't supported
    // by HSSFSimpleShape.

    public static final short       OBJECT_TYPE_LINE               = HSSFShapeTypes.Line;
    public static final short       OBJECT_TYPE_RECTANGLE          = HSSFShapeTypes.Rectangle;
    public static final short       OBJECT_TYPE_OVAL               = HSSFShapeTypes.Ellipse;
    public static final short       OBJECT_TYPE_ARC                = HSSFShapeTypes.Arc;
    //    public static final short       OBJECT_TYPE_CHART              = 5;
//    public static final short       OBJECT_TYPE_TEXT               = 6;
//    public static final short       OBJECT_TYPE_BUTTON             = 7;
    public static final short       OBJECT_TYPE_PICTURE            = HSSFShapeTypes.PictureFrame;

//    public static final short       OBJECT_TYPE_POLYGON            = 9;
//    public static final short       OBJECT_TYPE_CHECKBOX           = 11;
//    public static final short       OBJECT_TYPE_OPTION_BUTTON      = 12;
//    public static final short       OBJECT_TYPE_EDIT_BOX           = 13;
//    public static final short       OBJECT_TYPE_LABEL              = 14;
//    public static final short       OBJECT_TYPE_DIALOG_BOX         = 15;
//    public static final short       OBJECT_TYPE_SPINNER            = 16;
//    public static final short       OBJECT_TYPE_SCROLL_BAR         = 17;
//    public static final short       OBJECT_TYPE_LIST_BOX           = 18;
//    public static final short       OBJECT_TYPE_GROUP_BOX          = 19;
    public static final short       OBJECT_TYPE_COMBO_BOX          = HSSFShapeTypes.HostControl;
    public static final short       OBJECT_TYPE_COMMENT            = HSSFShapeTypes.TextBox;
    public static final short       OBJECT_TYPE_MICROSOFT_OFFICE_DRAWING = 30;

    public static final int WRAP_SQUARE = 0;
    public static final int WRAP_BY_POINTS = 1;
    public static final int WRAP_NONE = 2;

    private TextObjectRecord _textObjectRecord;

    public HSSFSimpleShape(EscherContainerRecord spContainer, ObjRecord objRecord, TextObjectRecord textObjectRecord) {
        super(spContainer, objRecord);
        this._textObjectRecord = textObjectRecord;
    }

    public HSSFSimpleShape(EscherContainerRecord spContainer, ObjRecord objRecord) {
        super(spContainer, objRecord);
    }

    public HSSFSimpleShape( HSSFShape parent, HSSFAnchor anchor)
    {
        super( parent, anchor );
        _textObjectRecord = createTextObjRecord();
    }

    protected TextObjectRecord getTextObjectRecord() {
        return _textObjectRecord;
    }

    protected TextObjectRecord createTextObjRecord(){
        TextObjectRecord obj = new TextObjectRecord();
        obj.setHorizontalTextAlignment(HORIZONTAL_TEXT_ALIGNMENT_CENTERED);
        obj.setVerticalTextAlignment(VERTICAL_TEXT_ALIGNMENT_CENTER);
        obj.setTextLocked(true);
        obj.setTextOrientation(TextObjectRecord.TEXT_ORIENTATION_NONE);
        obj.setStr(new HSSFRichTextString(""));
        return obj;
    }

    @Override
    protected EscherContainerRecord createSpContainer() {
        EscherContainerRecord spContainer = new EscherContainerRecord();
        spContainer.setRecordId( EscherContainerRecord.SP_CONTAINER );
        spContainer.setOptions( (short) 0x000F );

        EscherSpRecord sp = new EscherSpRecord();
        sp.setRecordId( EscherSpRecord.RECORD_ID );
        sp.setFlags( EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE );
        sp.setVersion((short) 0x2);

        EscherClientDataRecord clientData = new EscherClientDataRecord();
        clientData.setRecordId( EscherClientDataRecord.RECORD_ID );
        clientData.setOptions( (short) (0x0000) );

        EscherOptRecord optRecord = new EscherOptRecord();
        optRecord.setEscherProperty(new EscherSimpleProperty(EscherPropertyTypes.LINESTYLE__LINEDASHING, LINESTYLE_SOLID));
        optRecord.setEscherProperty( new EscherBoolProperty( EscherPropertyTypes.LINESTYLE__NOLINEDRAWDASH, 0x00080008));
//        optRecord.setEscherProperty(new EscherSimpleProperty(EscherProperties.LINESTYLE__LINEWIDTH, LINEWIDTH_DEFAULT));
        optRecord.setEscherProperty(new EscherRGBProperty(EscherPropertyTypes.FILL__FILLCOLOR, FILL__FILLCOLOR_DEFAULT));
        optRecord.setEscherProperty(new EscherRGBProperty(EscherPropertyTypes.LINESTYLE__COLOR, LINESTYLE__COLOR_DEFAULT));
        optRecord.setEscherProperty(new EscherBoolProperty(EscherPropertyTypes.FILL__NOFILLHITTEST, NO_FILLHITTEST_FALSE));
        optRecord.setEscherProperty( new EscherBoolProperty( EscherPropertyTypes.LINESTYLE__NOLINEDRAWDASH, 0x00080008));

        optRecord.setEscherProperty( new EscherShapePathProperty( EscherPropertyTypes.GEOMETRY__SHAPEPATH, EscherShapePathProperty.COMPLEX ) );
        optRecord.setEscherProperty(new EscherBoolProperty( EscherPropertyTypes.GROUPSHAPE__FLAGS, 0x080000));
        optRecord.setRecordId( EscherOptRecord.RECORD_ID );

        EscherTextboxRecord escherTextbox = new EscherTextboxRecord();
        escherTextbox.setRecordId(EscherTextboxRecord.RECORD_ID);
        escherTextbox.setOptions((short) 0x0000);

        spContainer.addChildRecord(sp);
        spContainer.addChildRecord(optRecord);
        spContainer.addChildRecord(getAnchor().getEscherAnchor());
        spContainer.addChildRecord(clientData);
        spContainer.addChildRecord(escherTextbox);
        return spContainer;
    }

    @Override
    protected ObjRecord createObjRecord() {
        ObjRecord obj = new ObjRecord();
        CommonObjectDataSubRecord c = new CommonObjectDataSubRecord();
        c.setLocked(true);
        c.setPrintable(true);
        c.setAutofill(true);
        c.setAutoline(true);
        EndSubRecord e = new EndSubRecord();

        obj.addSubRecord(c);
        obj.addSubRecord(e);
        return obj;
    }

    @Override
    protected void afterRemove(HSSFPatriarch patriarch) {
        patriarch.getBoundAggregate().removeShapeToObjRecord(getEscherContainer().getChildById(EscherClientDataRecord.RECORD_ID));
        if (null != getEscherContainer().getChildById(EscherTextboxRecord.RECORD_ID)){
            patriarch.getBoundAggregate().removeShapeToObjRecord(getEscherContainer().getChildById(EscherTextboxRecord.RECORD_ID));
        }
    }

    /**
     * @return the rich text string for this textbox.
     */
    public HSSFRichTextString getString() {
        return _textObjectRecord.getStr();
    }

    /**
     * @param string Sets the rich text string used by this object.
     */
    public void setString(RichTextString string) {
        //TODO add other shape types which can not contain text
        if (getShapeType() == 0 || getShapeType() == OBJECT_TYPE_LINE){
            throw new IllegalStateException("Cannot set text for shape type: "+getShapeType());
        }
        HSSFRichTextString rtr = (HSSFRichTextString) string;
        // If font is not set we must set the default one
        if (rtr.numFormattingRuns() == 0) rtr.applyFont((short) 0);
        TextObjectRecord txo = getOrCreateTextObjRecord();
        txo.setStr(rtr);
        if (string.getString() != null){
            setPropertyValue(new EscherSimpleProperty(EscherPropertyTypes.TEXT__TEXTID, string.getString().hashCode()));
        }
    }

    @Override
    void afterInsert(HSSFPatriarch patriarch){
        EscherAggregate agg = patriarch.getBoundAggregate();
        agg.associateShapeToObjRecord(getEscherContainer().getChildById(EscherClientDataRecord.RECORD_ID), getObjRecord());

        if (null != getTextObjectRecord()){
            agg.associateShapeToObjRecord(getEscherContainer().getChildById(EscherTextboxRecord.RECORD_ID), getTextObjectRecord());
        }
    }

    @Override
    protected HSSFShape cloneShape() {
        TextObjectRecord txo = null;
        EscherContainerRecord spContainer = new EscherContainerRecord();
        byte [] inSp = getEscherContainer().serialize();
        spContainer.fillFields(inSp, 0, new DefaultEscherRecordFactory());
        ObjRecord obj = (ObjRecord) getObjRecord().cloneViaReserialise();
        if (getTextObjectRecord() != null && getString() != null && null != getString().getString()){
            txo = (TextObjectRecord) getTextObjectRecord().cloneViaReserialise();
        }
        return new HSSFSimpleShape(spContainer, obj, txo);
    }


    /**
     * Gets the shape type.
     * @return  One of the OBJECT_TYPE_* constants.
     *
     * @see #OBJECT_TYPE_LINE
     * @see #OBJECT_TYPE_OVAL
     * @see #OBJECT_TYPE_RECTANGLE
     * @see #OBJECT_TYPE_PICTURE
     * @see #OBJECT_TYPE_COMMENT
     */
    public int getShapeType() {
        EscherSpRecord spRecord = getEscherContainer().getChildById(EscherSpRecord.RECORD_ID);
        return spRecord.getShapeType();
    }

    public int getWrapText(){
        EscherSimpleProperty property = getOptRecord().lookup(EscherPropertyTypes.TEXT__WRAPTEXT);
        return null == property ? WRAP_SQUARE : property.getPropertyValue();
    }

    public void setWrapText(int value){
        setPropertyValue(new EscherSimpleProperty(EscherPropertyTypes.TEXT__WRAPTEXT, false, false, value));
    }

    /**
     * @see HSSFShapeTypes
     * @param value - shapeType
     */
    public void setShapeType(int value){
        CommonObjectDataSubRecord cod = (CommonObjectDataSubRecord) getObjRecord().getSubRecords().get(0);
        cod.setObjectType(OBJECT_TYPE_MICROSOFT_OFFICE_DRAWING);
        EscherSpRecord spRecord = getEscherContainer().getChildById(EscherSpRecord.RECORD_ID);
        spRecord.setShapeType((short) value);
    }

    private TextObjectRecord getOrCreateTextObjRecord(){
        if (getTextObjectRecord() == null){
            _textObjectRecord = createTextObjRecord();
        }
        EscherTextboxRecord escherTextbox = getEscherContainer().getChildById(EscherTextboxRecord.RECORD_ID);
        if (null == escherTextbox){
            escherTextbox = new EscherTextboxRecord();
            escherTextbox.setRecordId(EscherTextboxRecord.RECORD_ID);
            escherTextbox.setOptions((short) 0x0000);
            getEscherContainer().addChildRecord(escherTextbox);
            getPatriarch().getBoundAggregate().associateShapeToObjRecord(escherTextbox, _textObjectRecord);
        }
        return _textObjectRecord;
    }

    @Override
    public int getShapeId(){
        return super.getShapeId();
    }
}
