/*
 * Copyright (C) 2015-2016 Département de l'Instruction Publique (DIP-SEM)
 *
 * Copyright (C) 2013 Open Education Foundation
 *
 * Copyright (C) 2010-2013 Groupement d'Intérêt Public pour
 * l'Education Numérique en Afrique (GIP ENA)
 *
 * This file is part of OpenBoard.
 *
 * OpenBoard is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License,
 * with a specific linking exception for the OpenSSL project's
 * "OpenSSL" library (or with modified versions of it that use the
 * same license as the "OpenSSL" library).
 *
 * OpenBoard is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with OpenBoard. If not, see <http://www.gnu.org/licenses/>.
 */




#if !defined Object_h
#define Object_h

#include "Utils.h"

#include <cmath>
#include <string>
#include <fstream>
#include <map>
#include <set>
#include <vector>
#include <utility>

namespace merge_lib
{
    //This class represents pdf objects, and defines methods for performing 
    //all necessary operations on pdf objects
    //Each object consists of two parts: content and object's number
    //<object number> <number> obj
    //<content>
    //endobj
    //Object can contain several links to other object. These objects has been named "children".
    //Each reference (child object) should be kept with it position(s) in object's content.
    //After each content modification, all references should be changed too.
    //This convention lighten the recalculation object numbers work.
    class Object
    {
    public:
       friend class PageElementHandler;       
       typedef std::vector<unsigned int> ReferencePositionsInContent;
       typedef std::pair<Object *, ReferencePositionsInContent > ChildAndItPositionInContent;
       typedef std::map <unsigned int, ChildAndItPositionInContent> Children;
       Object(unsigned int objectNumber, unsigned int generationNumber, const std::string & objectContent, 
           std::string fileName = "", std::pair<unsigned int, unsigned int> streamBounds = std::make_pair ((unsigned int)0,(unsigned int)0), bool hasStream = false
                  ):
       _number(objectNumber), _generationNumber(generationNumber), _oldNumber(objectNumber), _content(objectContent),_parents(),_children(),_isPassed(false),
           _streamBounds(streamBounds), _fileName(fileName), _hasStream(hasStream), _hasStreamInContent(false)
       {
       }
       virtual ~Object();
       Object *                    getClone(std::vector<Object *> & clones);
       void                        addChild(Object * child, const std::vector<unsigned int> childPositionsInContent);
       void                        addChild(const Children & children);
       ReferencePositionsInContent removeChild(Object * child);
       void                        forgetAboutChildren(unsigned int leftBound, unsigned int rightBound);
       Object *                    getChild(unsigned int objectNumber);
       bool                        findObject(const std::string & token, Object* & foundObject, unsigned int & tokenPositionInContent);
       std::vector<Object *>       getChildrenByBounds(unsigned int leftBound, unsigned int rightBound);
       std::vector<Object *>       getSortedByPositionChildren(unsigned int leftBound, unsigned int rightBound);
       void                        removeChildrenByBounds(unsigned int leftBound, unsigned int rightBound);
       const                       Children & getChildren();

       void                        removeHimself();

       unsigned int                getObjectNumber() const;
       unsigned int                getgenerationNumber() const;


       std::string &               getObjectContent();

       void                        setObjectContent(const std::string & objectContent);
       void                        appendContent(const std::string & addToContent);
       void                        eraseContent(unsigned int from, unsigned int size);
       void                        insertToContent(unsigned int position, const char * insertedStr, unsigned int length);
       void                        insertToContent(unsigned int position, const std::string & insertedStr);   

       //vector <object number, its size>
       void serialize(std::ofstream & out, std::map< unsigned int, std::pair<unsigned long long, unsigned int > > & sizesAndGenerationNumbers);

       void recalculateObjectNumbers(unsigned int & newNumber);

       bool isPassed()
       {
          return _isPassed;
       }
       void retrieveMaxObjectNumber(unsigned int & maxNumber);
       void resetIsPassed()
       {
          if(_isPassed)
             _isPassed = false;
          Children::iterator it;
          for ( it=_children.begin() ; it != _children.end(); it++ )
          {
             if((*it).second.first->isPassed())
                (*it).second.first->resetIsPassed();
          }

       }
       unsigned int getOldNumber()
       {
          return _oldNumber;
       }
       void setObjectNumber(unsigned int objNumber)
       {
          _number = objNumber;
          _oldNumber = objNumber;

       }
       bool getStream(std::string &);
       bool hasStream();
       bool getHeader(std::string &content);
       void forgetStreamInFile()
       {
            _hasStreamInContent = true;
            _hasStream = true;
       }

       std::string getNameSimpleValue(const std::string &content, const std::string &patten, size_t pos = 0);
       
       unsigned int getChildPosition(const Object * child); //throw (Exception)
       const std::set<Object *> & getParents()
       {
           return _parents;
       }
       
      Object* findPatternInObjOrParents(const std::string &pattern);

    private:
       //methods
       Object(const Object & copy);
       Object * _getClone(std::map<unsigned int, Object *> & clones);
       void _addChild(Object * child, const ReferencePositionsInContent & childPositionsInContent);
       void _setObjectNumber(unsigned int objectNumber);       
       void _addParent(Object * child);
       bool _findObject(const std::string & token, Object* & foundObject, unsigned int & tokenPositionInContent);
       void _serialize(std::ofstream  & out, const std::string & stream);
       void _recalculateObjectNumbers(unsigned int & maxNumber);
       void _recalculateReferencePositions(unsigned int changedReference, int displacement);
       void _retrieveMaxObjectNumber(unsigned int & maxNumber);
       void _serialize(std::ofstream & out, std::map<unsigned int, unsigned long long> & sizes);       
       bool _getStreamFromContent(std::string & stream);

       //members
       unsigned int                          _number;
       unsigned int                          _generationNumber;
       unsigned int                          _oldNumber;
       std::string                           _content;
       std::set <Object *>                   _parents;
       Children                              _children;
       bool                                  _isPassed;
       std::pair<unsigned int, unsigned int> _streamBounds;
       std::string                           _fileName;
       bool                                  _hasStream;
       bool                                  _hasStreamInContent;

    };
}
#endif