/////////////////////////////////////////////////////////////
//                                                         //
// Copyright (c) 2003-2017 by The University of Queensland //
// Centre for Geoscience Computing                         //
// http://earth.uq.edu.au/centre-geoscience-computing      //
//                                                         //
// Primary Business: Brisbane, Queensland, Australia       //
// Licensed under the Open Software License version 3.0    //
// http://www.apache.org/licenses/LICENSE-2.0              //
//                                                         //
/////////////////////////////////////////////////////////////


#ifndef ESYS_LSMGEOMETRYREADER_H
#define ESYS_LSMGEOMETRYREADER_H

#include "Foundation/vec3.h"
#include "Parallel/IterativeReader.h"
#include "Geometry/GeometryInfo.h"
#include "Geometry/SimpleParticleData.h"

#include <string>
#include <vector>
#include <iostream>

typedef std::vector<bool> BoolVector;
typedef std::vector<int>  IntVector;
typedef std::vector<Vec3> Vec3Vector;

/**
 * Earth Systems Science Computational Centre namespace.
 */
namespace esys
{
  /**
   * Lattice Solid Model namespace.
   */
  namespace lsm
  {
    class ParticleIterator : public IStreamIterator<SimpleParticleData>
    {
    public:
      typedef IStreamIterator<SimpleParticleData>::value_type value_type;

      ParticleIterator(std::istream &iStream, int numElements, bool is2d = true)
        : IStreamIterator<SimpleParticleData>(iStream, numElements),
          m_is2d(is2d)
      {
      }

    protected:
      virtual void readDataFromStream()
      {
        IStreamIterator<SimpleParticleData>::readDataFromStream();

        double mass = M_PI*m_data.getRadius() * m_data.getRadius();
        if (!m_is2d)
        {
          mass *= (4.0/3.0)*m_data.getRadius();
        }
        m_data.setMass(mass);
      }

    private:
      bool m_is2d;
    };
    
    /**
     * Creates iterator for parsing particle data from a stream.
     */
    class ParticleReader : public IterativeReader<ParticleIterator>
    {
    public:
      /**
       * Constructs reader.
       */
      ParticleReader(std::istream &iStream, bool is2d);

      /**
       * Reads from iStream to determine the number
       * of particles expected in the stream and constructs
       * an iterator.
       */
      virtual void initialise();
      
      /**
       * Returns particle type as string.
       */
      const std::string &getParticleType();
      
    protected:
      virtual ParticleIterator *createNewIterator();

    private:
      std::string m_particleType;
      bool        m_is2d;
    };

    /**
     * Represents a connections between a pair of particles.
     */
    class SimpleConnectionData
    {
    public:
      typedef SimpleParticleData::Id  Id;
      typedef SimpleParticleData::Tag Tag;

      SimpleConnectionData();

      SimpleConnectionData(Id p1Id, Id p2Id, Tag tag);

      bool operator==(const SimpleConnectionData &particleData) const;

      const Id &getP1Id() const;
      
      const Id &getP2Id() const;

      const Tag &getTag() const;

      void read(std::istream &istream);

      void write(std::ostream &write) const;

    private:
      Id  m_particle1Id;
      Id  m_particle2Id;
      Tag m_tag;
    };

    std::istream &operator>>(std::istream &iStream, SimpleConnectionData &connectionData);
    std::ostream &operator<<(std::ostream &oStream, const SimpleConnectionData &connectionData);

    /**
     * Creates iterator for parsing particle-connection data from a stream.
     */
    class ConnectionReader : public IterativeReader<IStreamIterator<SimpleConnectionData> >
    {
    public:
      /**
       * Constructs reader.
       */
      ConnectionReader(std::istream &iStream);

      /**
       * Reads from iStream to determine the number
       * of connections expected in the stream and
       * creates an iterator.
       */
      virtual void initialise();

    private:
    };

    /**
     * Objects of this class can be used to parse geometry (.geo) files
     * generated by gengeo.
     */
    class GeometryReader
    {
    public:
      typedef ParticleReader::Iterator   ParticleIterator;
      typedef ConnectionReader::Iterator ConnectionIterator;
      /**
       * Initialises the reader. Reads file meta header-info.
       *
       * @param fileName Name of geometry file.
       *
       * @throws std::runtime_error if file does not exist or is an
       *         unsuitable format.
       */
      GeometryReader(const std::string &fileName);

      /**
       * Initialises the reader. Reads file meta header-info.
       *
       * @param iStream Input stream from which geometry data is read.
       *
       * @throws std::runtime_error if stream data is in an
       *         unsuitable format.
       */
      GeometryReader(std::istream &iStream);

      /**
       *
       */
      virtual ~GeometryReader();

      /**
       * Returns the name of the file associated with this
       * geometry reader.
       */
      const std::string &getFileName() const;

      /**
       * Returns particle type as string.
       */
      const std::string &getParticleType();

      /**
       * Returns a GeometryInfo object containing data
       * associated with the file/stream of this reader.
       */
      const GeometryInfo &getGeometryInfo() const;
      
      /**
       * Returns iterator for enumerating particle data from file.
       */
      ParticleIterator &getParticleIterator();

      /**
       * Returns iterator for enumerating particle-connection data from file.
       */
      ConnectionIterator &getConnectionIterator();

    protected:
      /**
       * Reads/parses meta header-information from geometry file.
       */
      void initialise();

    private:
      GeometryReader(const GeometryReader &geoReader);
      GeometryReader &operator=(const GeometryReader &geoReader) const;

      class Impl;

      Impl *m_pImpl;
    };
  } // namespace lsm
} // namespace esys

#endif
