//  $Id$
using System;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using Lisp;

public class TileGroup {
    public string Name;
    public ArrayList Tiles = new ArrayList();

    public void Write(LispWriter writer) {
        writer.StartList("tilegroup");

        writer.Write("name", Name);
        writer.Write("tiles", Tiles);

        writer.EndList("tilegroup");
    }

    public void Parse(Lisp.Parser parser) {
        int d = parser.Depth;
        while(parser.Parse() && parser.Depth >= d) {
            if(parser.Depth == d+1) {
                if(parser.Type != Parser.LispType.SYMBOL)
                    throw new Exception("expected SYMBOL at supertux-tiles level, but got \"" + parser.StringValue + "\"");
                string symbol = parser.SymbolValue;
                parser.Parse();
                switch(symbol) {
                    case "name":
                        Name = parser.StringValue;
                        break;
                    case "tiles":
                        do {
                          Tiles.Add(parser.IntegerValue);
                        } while(parser.Parse()
                                && parser.Type == Parser.LispType.INTEGER);
                        break;
                    default:
                        Console.WriteLine("Unknown section " + symbol);
                        break;
                }
            }
        }
    }
}

public class TileSet {
    public const int TILE_WIDTH = 32;
    public const int TILE_HEIGHT = 32;

    private bool isNew = false;

    /// <summary>Whether version of tileset file is too new</summary>
    public bool IsNew {
	get {
		return isNew;
	}
    }

    public ArrayList Tiles = new ArrayList();
    public ArrayList TileGroups = new ArrayList();

    public void Write(string filename) {
        FileStream fs = new FileStream(filename, FileMode.Create);

        TextWriter tw = new StreamWriter(fs);
        LispWriter writer = new LispWriter(tw);

        writer.WriteComment("Generated by tiler");
        writer.StartList("supertux-tiles");
        foreach(TileGroup tilegroup in TileGroups) {
            tilegroup.Write(writer);
        }
        foreach(Tile tile in Tiles) {
            if(tile == null)
                continue;
            if(tile.ID >= 0)
                tile.Write(writer);
        }
        writer.EndList("supertux-tiles");
        tw.Close();
        fs.Close();
    }

    public void Parse(string filename) {
        FileStream fs = new FileStream(filename, FileMode.Open);
        StreamReader stream = new StreamReader(fs);

        Lisp.Parser parser = new Lisp.Parser(stream);
        parser.Parse();
        if(parser.Type != Parser.LispType.START_LIST)
            throw new Exception("Expected START_LIST");
        parser.Parse();
        if(parser.Type != Parser.LispType.SYMBOL)
            throw new Exception("Expected symbol");
        if(parser.SymbolValue != "supertux-tiles")
            throw new Exception("not a supertux tile files but " +
                    parser.SymbolValue);
        ParseTiles(parser);

        stream.Close();
        fs.Close();
    }

    public void ParseTiles(Lisp.Parser parser) {
	isNew = false;
        int d = parser.Depth;
        while(parser.Parse() && parser.Depth >= d) {
            if(parser.Depth == d && parser.Type != Parser.LispType.START_LIST) {
                Console.WriteLine("non-cons type in list...");
                continue;
            }

            if(parser.Depth == d+1) {
                if(parser.Type != Parser.LispType.SYMBOL) {
                    throw new Exception("Expected symbol in list element");
                }
                switch(parser.SymbolValue) {
                    case "properties":
                        SkipList(parser);
                        break;
                    case "tilegroup":
                        TileGroup tilegroup = new TileGroup();
                        tilegroup.Parse(parser);
                        TileGroups.Add(tilegroup);
                        break;
                    case "tile":
                        Tile tile = new Tile();
                        tile.Parse(parser);

                        while(tile.ID >= Tiles.Count)
                            Tiles.Add(null);
                        Tiles[tile.ID] = tile;
                        break;
                    case "tiles":
			ParseMoreTiles(parser);
			isNew = true;
                        break;
                   default:
                        throw new Exception("Unexpected listentry: " +
                                parser.SymbolValue);
                }
            }
        }
    }

	public void ParseMoreTiles(Lisp.Parser parser)
	{
		int blockWidth = 0;
		int blockHeight = 0;
		List<int> ids = new List<int>();
		List<int> attributes = new List<int>();
		List<int> datas = new List<int>();
		List<string> imageNames = new List<string>();
		float animFps = 0;

		int d = parser.Depth;
		while(parser.Parse() && parser.Depth >= d) {
			if(parser.Depth == d+1) {
				if(parser.Type != Parser.LispType.SYMBOL)
					throw new Exception("expected SYMBOL at supertux-tiles---tiles level, but got \"" + parser.StringValue + "\"");
				string symbol = parser.SymbolValue;
				parser.Parse();
				switch(symbol) {
					case "width":
						blockWidth = parser.IntegerValue;
						break;
					case "height":
						blockHeight = parser.IntegerValue;
						break;
					case "ids":
						Parser.ParseIntList(parser, ids);
						break;
					case "attributes":
						Parser.ParseIntList(parser, attributes);
						break;
					case "datas":
						Parser.ParseIntList(parser, datas);
						break;
					case "anim-fps":
						animFps = parser.FloatValue;
						break;
					case "image":
						int subDepth = parser.Depth;
						while(parser.Depth >= subDepth) {
							imageNames.Add(parser.StringValue);
							parser.Parse();
						}
						break;
					default:
						Console.WriteLine("Unknown tiles element " + symbol);
						break;
				}
			}
		}
		if(ids.Count != blockWidth * blockHeight)
			throw new ApplicationException("Must have width*height ids in tiles block, but found " + ids.Count.ToString());
		if((attributes.Count != blockWidth * blockHeight) && attributes.Count > 0)	//missing atributes == all-are-0-attributes
			throw new ApplicationException("Must have width*height attributes in tiles block");
		if((datas.Count != blockWidth * blockHeight) && datas.Count > 0)	//missing DATAs == all-are-0-DATAs
			throw new ApplicationException("Must have width*height DATAs in tiles block");

		int id = 0;
		for(int y = 0; y < blockHeight; ++y) {
			for(int x = 0; x < blockWidth; ++x) {
				if (ids[id] != 0) {
					Tile tile = new Tile();

					tile.Images = new ArrayList();
					foreach (string str in imageNames)
					{
						ImageRegion region = new ImageRegion();
						region.ImageFile = str;
						region.Region.X = x * TILE_WIDTH;
						region.Region.Y = y * TILE_HEIGHT;
						region.Region.Width = TILE_WIDTH;
						region.Region.Height = TILE_HEIGHT;
						tile.Images.Add(region);
					}
					tile.ID = ids[id];
					tile.Attributes = (attributes.Count > 0)?attributes[id]:0;	//missing atributes == all-are-0-attributes
					tile.Data = (datas.Count > 0)?datas[id]:0;	//missing DATAs == all-are-0-DATAs
					tile.AnimFps = animFps;

					while(Tiles.Count <= tile.ID)
						Tiles.Add(null);

					Tiles[tile.ID] = tile;
				}

				id++;
			}
		}
	}

	private void SkipList(Lisp.Parser parser)
	{
		int d = parser.Depth;
		while(parser.Parse() && parser.Depth >= d)
			;
	}
}
