#ifndef LEVEL_INTERFACE_NETWORK_WRITER
#define LEVEL_INTERFACE_NETWORK_WRITER

#include <string>
#include <dune/common/fvector.hh>
#include <dune/grid/common/mcmgmapper.hh>
#include <dune/fufem/boundaryiterator.hh>
//#include <dune/faultnetworks/levelinterfacenetwork.hh>

class LevelInterfaceNetworkWriter {
    private:
        std::string filePath_;

        //! Parameter for mapper class
        template<int dim>
        struct FaceMapperLayout
        {
            bool contains (Dune::GeometryType gt)
            {
                return gt.dim() == dim-1;
            }
        };

        template <typename Intersection>
        void writeIntersection(const Intersection& intersection, std::ofstream& file, const std::string colorTag) {
            typedef typename Dune::FieldVector<typename Intersection::ctype, Intersection::dimensionworld > GlobalCoordinates;

            const auto& geometry = intersection.geometry();

            std::vector<GlobalCoordinates> vertices;
            vertices.resize(geometry.corners());

            for (int i=0; i<geometry.corners(); i++) {
                const auto& vertex = geometry.corner(i);
                vertices[i] = vertex;
            }

            for (size_t i=0; i<vertices.size(); i++) {
                for (size_t j=i+1; j<vertices.size(); j++) {
                    file << "\\draw[" << colorTag << "] ";
                    writeVertex(vertices[i], file);
                    file << " -- ";
                    writeVertex(vertices[j], file);
                    file << ";%\n";
                }
            }
        }

        template <typename GlobalCoordinates>
        void writeVertex(const GlobalCoordinates& vertex, std::ofstream& file) {
            size_t lastIndex = vertex.size()-1;

            file << "(";
            for (size_t i=0; i<lastIndex; i++) {
                file << vertex[i] << ", ";
            }
            file << vertex[lastIndex] << ")";
        }

    public:
        LevelInterfaceNetworkWriter(const std::string filePath) :
            filePath_(filePath)
        {}

        void setTarget(const std::string filePath) {
            filePath_ = filePath;
        }

        // works only in 1D and 2D
        template <typename LevelInterfaceNetwork>
        void write(const LevelInterfaceNetwork& levelInterfaceNetwork, bool writeGrid = false) {
			typedef typename LevelInterfaceNetwork::GridType::LevelGridView GridView;
            typedef typename LevelInterfaceNetwork::Intersection Intersection;

            std::ofstream file (filePath_);

            if (file.is_open()) {
                // define custom MATLAB colors
                file << "\\definecolor{MATLABred}{RGB}{162,20,47}%\n";
                file << "\\definecolor{MATLABblue}{RGB}{0,114,189}%\n";
                file << "\\definecolor{MATLABgreen}{RGB}{60,140,40}%\n";
                file << "\\definecolor{MATLABorange}{RGB}{212,83,25}%\n";
                file << "\\definecolor{MATLAByellow}{RGB}{237,177,32}%\n";
                file << "\\definecolor{lightGray}{RGB}{211,211,211}%\n";
                file << "%\n";

                file << "\\colorlet{boundary}{black}%\n";
                file << "\\colorlet{grid}{lightGray}%\n";
                for (size_t i=0; i<=levelInterfaceNetwork.level(); i++) {

                    file << "\\colorlet{level" << i << "}{black}%\n";
                }
                file << "%\n";

                file << "\\begin{tikzpicture}[scale=\\scale]%\n";

                const GridView& gridView = levelInterfaceNetwork.levelGridView();

                std::string colorTag;

                if (writeGrid) {
                    typedef typename Dune::MultipleCodimMultipleGeomTypeMapper<GridView, FaceMapperLayout > FaceMapper;
                    FaceMapper intersectionMapper(gridView);
                    std::vector<bool> intersectionHandled(intersectionMapper.size(),false);

                    // boundary and grid intersections
                    for (const auto& elem:elements(gridView)) {
                        for (const auto& isect:intersections(gridView, elem)) {

                            if (intersectionHandled[intersectionMapper.subIndex(elem, isect.indexInInside(),1)])
                                continue;

                            intersectionHandled[intersectionMapper.subIndex(elem, isect.indexInInside(),1)] = true;

                            if (isect.boundary()) {
                                colorTag = "boundary, very thin";
                            } else if (!levelInterfaceNetwork.isInterfaceIntersection(isect)) {
                                colorTag = "grid, very thin";
                            }
                            writeIntersection(isect, file, colorTag);
                        }
                    }

                    // fault intersections
                    const std::vector<Intersection>& intersections = levelInterfaceNetwork.getIntersections();
                    const std::vector<int>& intersectionsLevels = levelInterfaceNetwork.getIntersectionsLevels();
                    for (size_t i=0; i<intersections.size(); i++) {
                        colorTag = "level" + std::to_string(intersectionsLevels[i]);
                        writeIntersection(intersections[i], file, colorTag);
                    }

                } else {
                    // domain boundary
                    BoundaryIterator<GridView> bIt(gridView, BoundaryIterator<GridView>::begin);
                    BoundaryIterator<GridView> bEnd(gridView, BoundaryIterator<GridView>::end);
                    for(; bIt!=bEnd; ++bIt) {
                        colorTag = "boundary, very thin";
                        writeIntersection(*bIt, file, colorTag);
                    }

                    // fault intersections
                    const std::vector<Intersection>& intersections = levelInterfaceNetwork.getIntersections();
                    const std::vector<int>& intersectionsLevels = levelInterfaceNetwork.getIntersectionsLevels();
                    for (size_t i=0; i<intersections.size(); i++) {
                        colorTag = "level" + std::to_string(intersectionsLevels[i]);
                        writeIntersection(intersections[i], file, colorTag);
                    }
                }

                file << "\\end{tikzpicture}%";
                file.close();
            } else {
                DUNE_THROW(Exception, "Unable to open " << filePath_ << " for writing!");
            }
        }
};
#endif