/* ====================================================================
   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.hslf.record;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;

import static org.apache.logging.log4j.util.Unbox.box;

/**
 * Master container for Document. There is one of these for every
 *  slideshow, and it holds lots of definitions, and some summaries.
 */

public final class Document extends PositionDependentRecordContainer
{
    private byte[] _header;
    private static long _type = 1000;

    // Links to our more interesting children
    private DocumentAtom documentAtom;
    private Environment environment;
    private PPDrawingGroup ppDrawing;
    private SlideListWithText[] slwts;
    private ExObjList exObjList; // Can be null

    /**
     * Returns the DocumentAtom of this Document
     */
    public DocumentAtom getDocumentAtom() { return documentAtom; }

    /**
     * Returns the Environment of this Notes, which lots of
     * settings for the document in it
     */
    public Environment getEnvironment() { return environment; }

    /**
     * Returns the PPDrawingGroup, which holds an Escher Structure
     * that contains information on pictures in the slides.
     */
    public PPDrawingGroup getPPDrawingGroup() { return ppDrawing; }

    /**
     * Returns the ExObjList, which holds the references to
     * external objects used in the slides. This may be null, if
     * there are no external references.
     *
     * @param create if true, create an ExObjList if it doesn't exist
     */
    public ExObjList getExObjList(boolean create) {
        if (exObjList == null && create) {
            exObjList = new ExObjList();
            addChildAfter(exObjList, getDocumentAtom());
        }
        return exObjList;
    }

    /**
     * Returns all the SlideListWithTexts that are defined for
     *  this Document. They hold the text, and some of the text
     *  properties, which are referred to by the slides.
     * This will normally return an array of size 2 or 3
     */
    public SlideListWithText[] getSlideListWithTexts() { return slwts; }

    /**
     * Returns the SlideListWithText that deals with the
     *  Master Slides
     */
    public SlideListWithText getMasterSlideListWithText() {
        for (SlideListWithText slwt : slwts) {
            if (slwt.getInstance() == SlideListWithText.MASTER) {
                return slwt;
            }
        }
        return null;
    }

    /**
     * Returns the SlideListWithText that deals with the
     *  Slides, or null if there isn't one
     */
    public SlideListWithText getSlideSlideListWithText() {
        for (SlideListWithText slwt : slwts) {
            if (slwt.getInstance() == SlideListWithText.SLIDES) {
                return slwt;
            }
        }
        return null;
    }
    /**
     * Returns the SlideListWithText that deals with the
     *  notes, or null if there isn't one
     */
    public SlideListWithText getNotesSlideListWithText() {
        for (SlideListWithText slwt : slwts) {
            if (slwt.getInstance() == SlideListWithText.NOTES) {
                return slwt;
            }
        }
        return null;
    }


    /**
     * Set things up, and find our more interesting children
     */
    /* package */ Document(byte[] source, int start, int len) {
        // Grab the header
        _header = Arrays.copyOfRange(source, start, start+8);

        // Find our children
        _children = Record.findChildRecords(source,start+8,len-8);

        // Our first one should be a document atom
        if(! (_children[0] instanceof DocumentAtom)) {
            throw new IllegalStateException("The first child of a Document must be a DocumentAtom");
        }
        documentAtom = (DocumentAtom)_children[0];

        // Find how many SlideListWithTexts we have
        // Also, grab the Environment and PPDrawing records
        //  on our way past
        int slwtcount = 0;
        for(int i=1; i<_children.length; i++) {
            if(_children[i] instanceof SlideListWithText) {
                slwtcount++;
            }
            if(_children[i] instanceof Environment) {
                environment = (Environment)_children[i];
            }
            if(_children[i] instanceof PPDrawingGroup) {
                ppDrawing = (PPDrawingGroup)_children[i];
            }
            if(_children[i] instanceof ExObjList) {
                exObjList = (ExObjList)_children[i];
            }
        }

        // You should only every have 1, 2 or 3 SLWTs
        //  (normally it's 2, or 3 if you have notes)
        // Complain if it's not
        if(slwtcount == 0) {
            LOG.atWarn().log("No SlideListWithText's found - there should normally be at least one!");
        }
        if(slwtcount > 3) {
            LOG.atWarn().log("Found {} SlideListWithTexts - normally there should only be three!", box(slwtcount));
        }

        // Now grab all the SLWTs
        slwts = new SlideListWithText[slwtcount];
        slwtcount = 0;
        for(int i=1; i<_children.length; i++) {
            if(_children[i] instanceof SlideListWithText) {
                slwts[slwtcount] = (SlideListWithText)_children[i];
                slwtcount++;
            }
        }
    }

    /**
     * Adds a new SlideListWithText record, at the appropriate
     *  point in the child records.
     */
    public void addSlideListWithText(SlideListWithText slwt) {
        // The new SlideListWithText should go in
        //  just before the EndDocumentRecord
        Record endDoc = _children[_children.length - 1];
        if(endDoc.getRecordType() == RecordTypes.RoundTripCustomTableStyles12.typeID) {
            // last record can optionally be a RoundTripCustomTableStyles12Atom
            endDoc = _children[_children.length - 2];
        }
        if(endDoc.getRecordType() != RecordTypes.EndDocument.typeID) {
            throw new IllegalStateException("The last child record of a Document should be EndDocument, but it was " + endDoc);
        }

        // Add in the record
        addChildBefore(slwt, endDoc);

        // Updated our cached list of SlideListWithText records
        int newSize = slwts.length + 1;
        SlideListWithText[] nl = new SlideListWithText[newSize];
        System.arraycopy(slwts, 0, nl, 0, slwts.length);
        nl[nl.length-1] = slwt;
        slwts = nl;
    }

    public void removeSlideListWithText(SlideListWithText slwt) {
        ArrayList<SlideListWithText> lst = new ArrayList<>();
        for(SlideListWithText s : slwts) {
            if(s != slwt) lst.add(s);
            else {
                removeChild(slwt);
            }
        }
        slwts = lst.toArray(new SlideListWithText[0]);
    }

    /**
     * We are of type 1000
     */
    public long getRecordType() { return _type; }

    /**
     * Write the contents of the record back, so it can be written
     *  to disk
     */
    public void writeOut(OutputStream out) throws IOException {
        writeOut(_header[0],_header[1],_type,_children,out);
    }
}
