using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace WTD_Editor
{
    public class TileSet
    {
/*        // inner class
        private class ChangeTable
        {
            // attributes
            public bool[][] table;
            public bool up { get { return table[1][0]; } }
            public bool down { get { return table[1][2]; } }
            public bool left { get { return table[0][1]; } }
            public bool right { get { return table[2][1]; } }
            public bool upleft { get { return table[0][0]; } }
            public bool upright { get { return table[2][0]; } }
            public bool downleft { get { return table[0][2]; } }
            public bool downright { get { return table[2][2]; } }

            public ChangeTable()
            {
                table = new bool[3][];
                for(int i = 0; i<3; i++)
                    table[i] = new bool[3];
            }
        }*/

        // constants
        public const int NUM_TILES = 128;
        public const int TILE_WIDTH = 16;
        public const int TILE_HEIGHT = 16;

        // attributes
        public string name;
        public int tile1;
        public int tile2;
        public int tile3;
        public int tile4;
        public List<Bitmap> tiles;
        public Bitmap bmp;
        private Random rand;

        // constructor
        public TileSet(Bitmap bmp, string name)
        {
            this.bmp = bmp;
            rand = new Random();
            tiles = new List<Bitmap>();
            Rectangle dest = new Rectangle(0, 0, TILE_WIDTH, TILE_HEIGHT);
            for (int i = 0; i < NUM_TILES; i++)
            {
                Rectangle source = new Rectangle((i * TILE_WIDTH) % 128, (((i * TILE_WIDTH) / 128) * TILE_HEIGHT), TILE_WIDTH, TILE_HEIGHT);
                Bitmap b_tile = new Bitmap(TILE_WIDTH, TILE_HEIGHT);
                Graphics g_draw = Graphics.FromImage(b_tile);
                g_draw.DrawImage(bmp, dest, source, GraphicsUnit.Pixel);
                g_draw.Dispose();
                tiles.Add(b_tile);
            }
            tile1 = 0;
            tile2 = 8;
            tile3 = 16;
            tile4 = 126;
            this.name = name;
        }

/*
        // return 1 if type x, 2 if border type x, 0 else
        private int isTypeX(TileMap map, Point pos, int type)
        {
            switch (type)
            {
                case 1:
                    return isType1(map, pos);
                case 2:
                    return isType2(map, pos);
                case 3:
                    return isType3(map, pos);
                case 4:
                    return isType4(map, pos);
            }
            return 0;
        }

        // return 1 if type 1, 2 if border type 1, 0 else
        private int isType1(TileMap map, Point pos)
        {
            if (!(pos.X >= 0 && pos.X < map.width && pos.Y >= 0 && pos.Y < map.height))
                return 0;

            int[] type1 = {
                0, 1, 2, 3, 4, 5, 6, 7,
                24, 25, 26, 27, 28, 29, 30, 31,
                32, 33, 34, 35, 36, 37, 38, 39,
                64, 65,                 70, 71,
                72, 73,                 78, 79,
                80, 81,
                88, 89,
                96, 97,
                104, 105,
                112, 113,
                120, 121,
            };
            int tilenum = map.tilemap[pos.X][pos.Y];

            int result = -1;
            for (int i = 0; i < type1.GetLength(0); i++)
            {
                if (tilenum == type1[i])
                {
                    result = i;
                    break;
                }
            }

            if (result >= 8)
                return 2;
            else if (result >= 0 && result < 8)
                return 1;
            else 
                return 0;
        }

        // return 1 if type 2, 2 if border type 2, 0 else
        private int isType2(TileMap map, Point pos)
        {
            if (!(pos.X >= 0 && pos.X < map.width && pos.Y >= 0 && pos.Y < map.height))
                return 0;

            int[] type2 = {
                8, 9, 10, 11, 12, 13, 14, 15,
                24, 25, 26, 27, 28, 29, 30, 31,
                32, 33, 34, 35, 36, 37, 38, 39,
                40, 41, 42, 43, 44, 45, 46, 47,
                48, 49, 50, 51, 52, 53, 54, 55,
                56, 57, 58, 59, 60, 61, 62, 63,
                        66, 67, 68, 69, 70, 71,
                        74, 75, 76, 77, 78, 79,
                        82, 83, 84, 85, 86, 87,
                        90, 91, 92, 93, 94, 95,
                        98, 99, 100, 101, 102, 103,
                        106, 107, 108, 109, 110, 111,
                        114, 115, 116, 117,
                        122, 123, 124, 125,
            };
            int tilenum = map.tilemap[pos.X][pos.Y];

            int result = -1;
            for (int i = 0; i < type2.GetLength(0); i++)
            {
                if (tilenum == type2[i])
                {
                    result = i;
                    break;
                }
            }
            if (result >= 8)
                return 2;
            else if (result >= 0 && result < 8)
                return 1;
            else
                return 0;
        }

        // return 1 if type 3, 2 if border type 3, 0 else
        private int isType3(TileMap map, Point pos)
        {
            if (!(pos.X >= 0 && pos.X < map.width && pos.Y >= 0 && pos.Y < map.height))
                return 0;

            int[] type3 = {
                16, 17, 18, 19, 20, 21, 22, 23,
                40, 41, 42, 43, 44, 45, 46, 47,
                48, 49, 50, 51, 52, 53, 54, 55,
                56, 57, 58, 59, 60, 61, 62, 63,
                        66, 67, 68, 
                        74, 75, 76, 
                        82, 83, 84, 
                        90, 91, 92, 
                        98, 99, 100, 
                        106, 107, 108, 
                        114, 115, 116, 
                        122, 123, 124, 
            };
            int tilenum = map.tilemap[pos.X][pos.Y];

            int result = -1;
            for (int i = 0; i < type3.GetLength(0); i++)
            {
                if (tilenum == type3[i])
                {
                    result = i;
                    break;
                }
            }
            if (result >= 8)
                return 2;
            else if (result >= 0 && result < 8)
                return 1;
            else
                return 0;
        }

        // return 1 if type 4, 2 if border type 4, 0 else
        private int isType4(TileMap map, Point pos)
        {
            if (!(pos.X >= 0 && pos.X < map.width && pos.Y >= 0 && pos.Y < map.height))
                return 0;

            if (map.tilemap[pos.X][pos.Y] >= 128)
                return 1;

            int[] type4 = {
                        69,
                        77,
                        85, 86, 87,
                        93, 94, 95,
                        101, 102, 103,
                        109, 110, 111,
                        117,
                        125,
            };
            int tilenum = map.tilemap[pos.X][pos.Y];

            int result = -1;
            for (int i = 0; i < type4.GetLength(0); i++)
            {
                if (tilenum == type4[i])
                {
                    result = i;
                    break;
                }
            }
            if (result >= 0)
                return 2;
            else
                return 0;
        }

        private void changeTile(TileMap map, Point pos, int tile_num, int tiletype)
        {
            if (pos.X >= 0 && pos.X < map.width && pos.Y >= 0 && pos.Y < map.height)
            {
                map.tilemap[pos.X][pos.Y] = tile_num;
                ChangeTable changes = new ChangeTable();
                changes.table[1][1] = false;    // no change

                for (int j = -1; j < 2; j++)
                    for (int i = -1; i < 2; i++)
                    {
                        if (i != 0 && j != 0)
                        {
                            if (isTypeX(map, new Point(pos.X + i, pos.Y + j), tiletype) == 0)
                            {
                                changes.table[i + 1][j + 1] = true;
                                if (i == -1)
                                    if (j == -1) {
                                        changes.table[1][0] = true;
                                        changes.table[0][1] = true;
                                    } else if (j == 0) {
                                        changes.table[0][0] = true;
                                        changes.table[0][2] = true;
                                    } else {
                                        changes.table[0][1] = true;
                                        changes.table[1][2] = true;
                                    }
                                else if (i == 0)
                                    if (j == -1) {
                                        changes.table[0][0] = true;
                                        changes.table[2][0] = true;
                                    } else {
                                        changes.table[0][2] = true;
                                        changes.table[2][2] = true;
                                    }
                                else
                                    if (j == -1) {
                                        changes.table[1][0] = true;
                                        changes.table[2][1] = true;
                                    } else if (j == 0) {
                                        changes.table[2][0] = true;
                                        changes.table[2][2] = true;
                                    } else {
                                        changes.table[2][1] = true;
                                        changes.table[1][2] = true;
                                    }
                            }
                            else if (isTypeX(map, new Point(pos.X + i, pos.Y + j), tile_num) == 2)
                            {
                                changes.table[i + 1][j + 1] = true;
                            }
                        }
                    }

                putAngleChangeTile(map, 0, new Point(pos.X - 1, pos.Y - 1), changes, tiletype);
                putAngleChangeTile(map, 1, new Point(pos.X, pos.Y - 1), changes, tiletype);
                putAngleChangeTile(map, 2, new Point(pos.X + 1, pos.Y - 1), changes, tiletype);
                putAngleChangeTile(map, 3, new Point(pos.X - 1, pos.Y), changes, tiletype);
                putAngleChangeTile(map, 4, new Point(pos.X + 1, pos.Y), changes, tiletype);
                putAngleChangeTile(map, 5, new Point(pos.X - 1, pos.Y + 1), changes, tiletype);
                putAngleChangeTile(map, 6, new Point(pos.X, pos.Y + 1), changes, tiletype);
                putAngleChangeTile(map, 7, new Point(pos.X + 1, pos.Y + 1), changes, tiletype);
            }
        }

        private void putAngleChangeTile(TileMap map, int anglepos, Point pos, ChangeTable changes, int tile_num)
        {
            if (pos.X >= 0 && pos.X < map.width && pos.Y >= 0 && pos.Y < map.height)
            {
                // positions :
                // 0 1 2
                // 3   4
                // 5 6 7
                switch (anglepos)
                {
                    case 0:
                        if (changes.upleft)
                        {
                            if (changes.up)
                            {
                                if (changes.left)
                                {
                                    putTile(map, new Point(pos.X, pos.Y), 70);
                                }
                                else
                                {

                                }
                            }
                            else if (changes.left)
                            {

                            }
                            else
                            {
                                putTile(map, new Point(pos.X, pos.Y), 121);
                            }
                        }
                        break;
                    case 1:

                        break;
                    case 2:

                        break;
                    case 3:

                        break;
                    case 4:

                        break;
                    case 5:

                        break;
                    case 6:

                        break;
                    case 7:

                        break;
                }
                

                if (changes.up) 
                {


                }
                else if (changes.down)
                {

                }
                else
                {


                }

            }
        }

        private void putAngleTile(TileMap map, Point pos, int tiletype)
        {
            bool left = false, right = false, up = false, down = false;
            bool left2 = false, right2 = false, up2 = false, down2 = false;

            switch (tiletype)
            {
                case 0:     // water
                    if (isType1(map, new Point(pos.X + 1, pos.Y)) == 0) right = true;
                    if (isType1(map, new Point(pos.X, pos.Y + 1)) == 0) down = true;
                    if (isType1(map, new Point(pos.X, pos.Y - 1)) == 0) up = true;
                    if (isType1(map, new Point(pos.X - 1, pos.Y)) == 0) left = true;
                    if (isType1(map, new Point(pos.X + 1, pos.Y)) == 2) right2 = true;
                    if (isType1(map, new Point(pos.X, pos.Y + 1)) == 2) down2 = true;
                    if (isType1(map, new Point(pos.X, pos.Y - 1)) == 2) up2 = true;
                    if (isType1(map, new Point(pos.X - 1, pos.Y)) == 2) left2 = true;

                    if (left2)
                        if(up2)
                            if(right && down)
                                // bottom-right corner
                                putTile(map, new Point(pos.X, pos.Y), 79);
                            else
                                // bottom-right empty
                                putTile(map, new Point(pos.X, pos.Y), 121);

                        else if(down2)
                            if(up && right)
                                // upper-right corner
                                putTile(map, new Point(pos.X, pos.Y), 71);
                            else
                                // upper-right empty
                                putTile(map, new Point(pos.X, pos.Y), 65);

                        else if (right2)
                        {
                            if (down)
                                // bottom corner
                                putTile(map, new Point(pos.X, pos.Y), 25 + rand.Next(0, 6));
                            else if (up)
                                // upper corner
                                putTile(map, new Point(pos.X, pos.Y), 33 + rand.Next(0, 6));
                        }

                        else
                        {
                            if (down)
                                // upper-right empty
                                putTile(map, new Point(pos.X, pos.Y), 65);
                            if(up)
                                // bottom-right empty
                                putTile(map, new Point(pos.X, pos.Y), 121);
                        }


                    else if(right2)
                        if(up2)
                            if(left && down)
                                // bottom-left corner
                                putTile(map, new Point(pos.X, pos.Y), 78);
                            else
                                // bottom-left empty
                                putTile(map, new Point(pos.X, pos.Y), 120);

                        else if(down2)
                            if(left && up)
                                // upper-left corner
                                putTile(map, new Point(pos.X, pos.Y ), 70);
                            else
                                // upper-left empty
                                putTile(map, new Point(pos.X, pos.Y), 64);

                        else if(left2) {
                            if(down)
                                // bottom corner
                                putTile(map, new Point(pos.X, pos.Y), 25 + rand.Next(0, 6));
                            else if(up)
                                // upper corner
                                putTile(map, new Point(pos.X, pos.Y), 33 + rand.Next(0, 6));
                        }

                        else
                        {
                            if (down)
                                // upper-left empty
                                putTile(map, new Point(pos.X, pos.Y), 64);
                            if (up)
                                // bottom-left empty
                                putTile(map, new Point(pos.X, pos.Y), 120);
                        }
                

                    else

                        if(up2) {
                            if(right)
                                // right corner
                                putTile(map, new Point(pos.X, pos.Y), 72 + rand.Next(0, 6) * 8);
                            else if(left)
                                // left corner
                                putTile(map, new Point(pos.X, pos.Y), 73 + rand.Next(0, 6) * 8);
                        }
                        else if(down2) {
                            if(right)
                                // right corner
                                putTile(map, new Point(pos.X, pos.Y), 72 + rand.Next(0, 6) * 8);
                            else if(left)
                                // left corner
                                putTile(map, new Point(pos.X, pos.Y), 73 + rand.Next(0, 6) * 8);
                        }
                        else
                            // no corner
                            putTile(map, new Point(pos.X, pos.Y), rand.Next(0, 8));








                    /*


                        if (down)
                            if(left)
                                if(right)

                                else

                            else

                            putTile(map, new Point(pos.X + 1, pos.Y + 1), 79);
                        else
                            putTile(map, new Point(pos.X + 1, pos.Y + 1), 120);
                    else
                        if (down)
                            putTile(map, new Point(pos.X + 1, pos.Y + 1), 65);
                    */
/*                    
                    break;

                case 1:     // grass

                    break;

                case 2:     // path


                    break;

                case 3:     // level 1


                    break;
            }

        }


        public void putAdaptedTile(TileMap map, Point pos, int tiletype)
        {
            switch (tiletype)
            {
                case 0:     // water
                    changeTile(map, pos, rand.Next(0, 8), tiletype);
                    map.tilemap[pos.X][pos.Y] = rand.Next(0, 8);
                    /*
                    if (isType1(map, new Point(pos.X + 1, pos.Y)) != 1)
//                        putAngleTile(map, new Point(pos.X + 1, pos.Y), tiletype);
                        if (isType1(map, new Point(pos.X + 2, pos.Y)) == 0)
                            putTile(map, new Point(pos.X + 1, pos.Y), 72 + rand.Next(0, 6) * 8);
                        else if (isType1(map, new Point(pos.X + 2, pos.Y)) == 2)
                            if (isType1(map, new Point(pos.X + 1, pos.Y - 1)) == 0)
                                putTile(map, new Point(pos.X + 1, pos.Y), 121);
                            else if (isType1(map, new Point(pos.X + 1, pos.Y + 1)) == 0)
                                putTile(map, new Point(pos.X + 1, pos.Y), 65);
                        else
                            putTile(map, new Point(pos.X + 1, pos.Y), rand.Next(0, 8));

                    if (isType1(map, new Point(pos.X, pos.Y + 1)) != 1)
//                        putAngleTile(map, new Point(pos.X, pos.Y + 1), tiletype);
                        if (isType1(map, new Point(pos.X, pos.Y + 2)) == 0)
                            putTile(map, new Point(pos.X, pos.Y + 1), 25 + rand.Next(0, 6));
                        else if (isType1(map, new Point(pos.X, pos.Y + 2)) == 2)
                            if (isType1(map, new Point(pos.X + 1, pos.Y + 1)) == 0)
                                putTile(map, new Point(pos.X, pos.Y + 1), 64);
                            else if (isType1(map, new Point(pos.X - 1, pos.Y + 1)) == 0)
                                putTile(map, new Point(pos.X, pos.Y + 1), 65);
                        else
                            putTile(map, new Point(pos.X, pos.Y + 1), rand.Next(0, 8));

                    if (isType1(map, new Point(pos.X, pos.Y - 1)) != 1)
//                        putAngleTile(map, new Point(pos.X, pos.Y - 1), tiletype);
                        if (isType1(map, new Point(pos.X, pos.Y - 2)) == 0)
                            putTile(map, new Point(pos.X, pos.Y - 1), 33 + rand.Next(0, 6));
                        else if (isType1(map, new Point(pos.X, pos.Y - 2)) == 2)
                            if (isType1(map, new Point(pos.X + 1, pos.Y - 1)) == 0)
                                putTile(map, new Point(pos.X, pos.Y - 1), 120);
                            else if (isType1(map, new Point(pos.X - 1, pos.Y - 1)) == 0)
                                putTile(map, new Point(pos.X, pos.Y - 1), 121);
                        else
                            putTile(map, new Point(pos.X, pos.Y - 1), rand.Next(0, 8));

                    if (isType1(map, new Point(pos.X - 1, pos.Y)) != 1)
//                        putAngleTile(map, new Point(pos.X - 1, pos.Y), tiletype);
                        if (isType1(map, new Point(pos.X - 2, pos.Y)) == 0)
                            putTile(map, new Point(pos.X - 1, pos.Y), 73 + rand.Next(0, 6) * 8);
                        else if (isType1(map, new Point(pos.X - 2, pos.Y)) == 2)
                            if (isType1(map, new Point(pos.X - 1, pos.Y - 1)) == 0)
                                putTile(map, new Point(pos.X - 1, pos.Y), 120);
                            else if (isType1(map, new Point(pos.X - 1, pos.Y + 1)) == 0)
                                putTile(map, new Point(pos.X - 1, pos.Y), 64);
                        else
                            putTile(map, new Point(pos.X - 1, pos.Y), rand.Next(0, 8));


                    putAngleTile(map, new Point(pos.X + 1, pos.Y + 1), tiletype);
                    putAngleTile(map, new Point(pos.X + 1, pos.Y - 1), tiletype);
                    putAngleTile(map, new Point(pos.X - 1, pos.Y + 1), tiletype);
                    putAngleTile(map, new Point(pos.X - 1, pos.Y - 1), tiletype);
                    */
/*                    break;

                case 1:     // grass
                    map.tilemap[pos.X][pos.Y] = rand.Next(8, 16);
                    if (isType4(map, pos) != 0)
                        map.tilemap[pos.X][pos.Y] += 128;


                    break;

                case 2:     // path
                    map.tilemap[pos.X][pos.Y] = rand.Next(16, 24);
                    if (isType4(map, pos) != 0)
                        map.tilemap[pos.X][pos.Y] += 128;



                    break;

                case 3:     // level 1
                    map.tilemap[pos.X][pos.Y] = rand.Next(0, 8) + 128;



                    break;
            }





        }

        public void drawAdaptedTile(Graphics gfx, TileMap map, Point pos, int tiletype)
        {
            Rectangle src = new Rectangle(0, 0, TILE_WIDTH, TILE_HEIGHT);
            putAdaptedTile(map, pos, tiletype);

            for (int i = -1; i < 2; i++)
                for (int j = -1; j < 2; j++)
                    if (pos.X+j >= 0 && pos.X+j < map.width && pos.Y+i >= 0 && pos.Y+i < map.height)
                        gfx.DrawImage(tiles[(map.tilemap[pos.X+j][pos.Y+i] % 128)], new Rectangle((pos.X+j) * TILE_WIDTH, (pos.Y+i) * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT), src, GraphicsUnit.Pixel);

            gfx.Dispose();
        }
*/


        // generate a number from 1 to 7, with low chances for 4 & 5 & very low for 6 & 7
        private int randomDistrib()
        {
            const int CHANCES_DIVISOR_1 = 2;
            const int CHANCES_DIVISOR_2 = 16;
            int rnd = rand.Next(0, CHANCES_DIVISOR_2 * 4 + CHANCES_DIVISOR_1 * 2 + 2);
            if (rnd < CHANCES_DIVISOR_2 * 4)
                return rnd / CHANCES_DIVISOR_2;
            else if (rnd < CHANCES_DIVISOR_2 * 4 + CHANCES_DIVISOR_1 * 2)
                return 4 + (rnd - CHANCES_DIVISOR_2 * 4) / CHANCES_DIVISOR_1;
            else
                return 6 + rnd - (CHANCES_DIVISOR_2 * 4 + CHANCES_DIVISOR_1 * 2);
        }

        public void putRandomBaseTile(TileMap map, Point pos, int tile_num)
        {
            if (pos.X >= 0 && pos.X < map.width && pos.Y >= 0 && pos.Y < map.height)
            {
                int base_tile = (tile_num / 8) * 8;
                map.tilemap[pos.X][pos.Y] = base_tile + randomDistrib();
            }
        }

        public void putAndDrawRandomBaseTile(Graphics gfx, TileMap map, Point pos, int tile_num)
        {
            putRandomBaseTile(map, pos, tile_num);
            drawTile(gfx, map, pos);
        }

        public void putAndDrawTile(Graphics gfx, TileMap map, Point pos, int tile_num)
        {
            putTile(map, pos, tile_num);
            drawTile(gfx, map, pos);
        }

        public void putTile(TileMap map, Point pos, int tile_num)
        {
            if (pos.X >= 0 && pos.X < map.width && pos.Y >= 0 && pos.Y < map.height)
                map.tilemap[pos.X][pos.Y] = tile_num;
        }

        public void drawTile(Graphics gfx, TileMap map, Point pos)
        {
            Rectangle src = new Rectangle(0, 0, TILE_WIDTH, TILE_HEIGHT);
            Rectangle dest = new Rectangle(pos.X * TILE_WIDTH, pos.Y * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT);

            if (pos.X >= 0 && pos.X < map.width && pos.Y >= 0 && pos.Y < map.height)
                gfx.DrawImage(tiles[map.tilemap[pos.X][pos.Y]], dest, src, GraphicsUnit.Pixel);
        }

        public void drawTile(Graphics gfx, TileMap map, Point pos, Point to)
        {
            Rectangle src = new Rectangle(0, 0, TILE_WIDTH, TILE_HEIGHT);
            Rectangle dest = new Rectangle(to.X * TILE_WIDTH, to.Y * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT);

            if (pos.X >= 0 && pos.X < map.width && pos.Y >= 0 && pos.Y < map.height)
                gfx.DrawImage(tiles[map.tilemap[pos.X][pos.Y]], dest, src, GraphicsUnit.Pixel);
        }

        override public string ToString()
        {
            return this.name;
        }
    }
}
