/*
 * Copyright (c) Contributors
 * See CONTRIBUTORS.TXT for a full list of copyright holders.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSimulator Project nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

using System.Drawing;
using System.Drawing.Imaging;

namespace PrimMesher
{

    public class SculptMesh
    {
        public List<Coord> coords;
        public List<Face> faces;

        public enum SculptType { sphere = 1, torus = 2, plane = 3, cylinder = 4 };


        public SculptMesh(Bitmap sculptBitmap, SculptType sculptType, int lod, bool mirror, bool invert)
        {
            if (mirror)
                invert = !invert;

            SculptMap smap = new SculptMap(sculptBitmap, lod);

            List<List<Coord>> rows = smap.ToRows(mirror);

            _SculptMesh(rows, sculptType, invert);
        }

        private void _SculptMesh(List<List<Coord>> rows, SculptType sculptType, bool invert)
        {
            coords = new List<Coord>();
            faces = new List<Face>();

            sculptType = (SculptType)(((int)sculptType) & 0x07);

            int width = rows[0].Count;

            int p1, p2, p3, p4;

            int imageX, imageY;

            if (sculptType != SculptType.plane)
            {
                if (rows.Count % 2 == 0)
                {
                    for (int rowNdx = 0; rowNdx < rows.Count; rowNdx++)
                        rows[rowNdx].Add(rows[rowNdx][0]);
                }
                else
                {
                    int lastIndex = rows[0].Count - 1;

                    for (int i = 0; i < rows.Count; i++)
                        rows[i][0] = rows[i][lastIndex];
                }
            }

            Coord topPole = rows[0][width / 2];
            Coord bottomPole = rows[rows.Count - 1][width / 2];

            if (sculptType == SculptType.sphere)
            {
                if (rows.Count % 2 == 0)
                {
                    int count = rows[0].Count;
                    List<Coord> topPoleRow = new List<Coord>(count);
                    List<Coord> bottomPoleRow = new List<Coord>(count);

                    for (int i = 0; i < count; i++)
                    {
                        topPoleRow.Add(topPole);
                        bottomPoleRow.Add(bottomPole);
                    }
                    rows.Insert(0, topPoleRow);
                    rows.Add(bottomPoleRow);
                }
                else
                {
                    int count = rows[0].Count;

                    List<Coord> topPoleRow = rows[0];
                    List<Coord> bottomPoleRow = rows[rows.Count - 1];

                    for (int i = 0; i < count; i++)
                    {
                        topPoleRow[i] = topPole;
                        bottomPoleRow[i] = bottomPole;
                    }
                }
            }

            if (sculptType == SculptType.torus)
                rows.Add(rows[0]);

            int coordsDown = rows.Count;
            int coordsAcross = rows[0].Count;

            float widthUnit = 1.0f / (coordsAcross - 1);
            float heightUnit = 1.0f / (coordsDown - 1);

            for (imageY = 0; imageY < coordsDown; imageY++)
            {
                int rowOffset = imageY * coordsAcross;

                for (imageX = 0; imageX < coordsAcross; imageX++)
                {
                    /*
                    *   p1-----p2
                    *   | \ f2 |
                    *   |   \  |
                    *   | f1  \|
                    *   p3-----p4
                    */

                    p4 = rowOffset + imageX;
                    p3 = p4 - 1;

                    p2 = p4 - coordsAcross;
                    p1 = p3 - coordsAcross;

                    this.coords.Add(rows[imageY][imageX]);

                    if (imageY > 0 && imageX > 0)
                    {
                        Face f1, f2;

                            if (invert)
                            {
                                f1 = new Face(p1, p4, p3);
                                f2 = new Face(p1, p2, p4);
                            }
                            else
                            {
                                f1 = new Face(p1, p3, p4);
                                f2 = new Face(p1, p4, p2);
                            }

                        faces.Add(f1);
                        faces.Add(f2);
                    }
                }
            }
        }

        /// <summary>
        /// Duplicates a SculptMesh object. All object properties are copied by value, including lists.
        /// </summary>
        /// <returns></returns>
        public SculptMesh Copy()
        {
            return new SculptMesh(this);
        }

        public SculptMesh(SculptMesh sm)
        {
            coords = new List<Coord>(sm.coords);
            faces = new List<Face>(sm.faces);
        }

        public void Scale(float x, float y, float z)
        {
            int i;
            int numVerts = this.coords.Count;

            Coord m = new Coord(x, y, z);
            for (i = 0; i < numVerts; i++)
                this.coords[i] *= m;
        }

        public void DumpRaw(String path, String name, String title)
        {
            if (path == null)
                return;
            String fileName = name + "_" + title + ".raw";
            String completePath = System.IO.Path.Combine(path, fileName);
            using(StreamWriter sw = new StreamWriter(completePath))
            {
                for (int i = 0; i < faces.Count; i++)
                {
                    sw.Write(coords[faces[i].v1].ToString());
                    sw.Write(coords[faces[i].v2].ToString());
                    sw.WriteLine(coords[faces[i].v3].ToString());
                }
            }
        }
    }
}
