/* ====================================================================
   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 org.apache.poi.util.POILogger;

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

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

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 (int i = 0; i < slwts.length; i++) {
            if(slwts[i].getInstance() == SlideListWithText.MASTER) {
                return slwts[i];
            }
        }
        return null;
    }

	/**
	 * Returns the SlideListWithText that deals with the
	 *  Slides, or null if there isn't one
	 */
	public SlideListWithText getSlideSlideListWithText() {
        for (int i = 0; i < slwts.length; i++) {
            if(slwts[i].getInstance() == SlideListWithText.SLIDES) {
                return slwts[i];
            }
        }
		return null;
    }
	/**
	 * Returns the SlideListWithText that deals with the
	 *  notes, or null if there isn't one
	 */
	public SlideListWithText getNotesSlideListWithText() {
        for (int i = 0; i < slwts.length; i++) {
            if(slwts[i].getInstance() == SlideListWithText.NOTES) {
                return slwts[i];
            }
        }
		return null;
    }


	/**
	 * Set things up, and find our more interesting children
	 */
	protected Document(byte[] source, int start, int len) {
		// Grab the header
		_header = new byte[8];
		System.arraycopy(source,start,_header,0,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) {
			logger.log(POILogger.WARN, "No SlideListWithText's found - there should normally be at least one!");
		}
		if(slwtcount > 3) {
			logger.log(POILogger.WARN, "Found " + slwtcount + " SlideListWithTexts - normally there should only be three!");
		}

		// 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.RoundTripCustomTableStyles12Atom.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[lst.size()]);
    }

	/**
	 * 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);
	}
}
