//
// Copyright 2020 Electronic Arts Inc.
//
// The Command & Conquer Map Editor and corresponding source code is free 
// software: you can redistribute it and/or modify it under the terms of 
// the GNU General Public License as published by the Free Software Foundation, 
// either version 3 of the License, or (at your option) any later version.

// The Command & Conquer Map Editor and corresponding source code is distributed 
// in the hope that it will be useful, but with permitted additional restrictions 
// under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 
// distributed with this program. You should have received a copy of the 
// GNU General Public License along with permitted additional restrictions 
// with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MobiusEditor.Model
{
    public class CellMetrics
    {
        public static readonly FacingType[] AdjacentFacings = new FacingType[] {
            FacingType.North, FacingType.NorthEast, FacingType.East, FacingType.SouthEast, FacingType.South, FacingType.SouthWest, FacingType.West, FacingType.NorthWest
        };

        public int Width { get; private set; }

        public int Height { get; private set; }

        public Point TopLeft => Point.Empty;

        public Size Size => new Size(Width, Height);

        public Rectangle Bounds => new Rectangle(TopLeft, Size);

        public int Length => Width * Height;

        public bool Contains(Point location) => ((location.X >= 0) && (location.X < Width) && (location.Y >= 0) && (location.Y < Height));

        public bool Contains(int cell) => ((cell >= 0) && (cell < Length));

        public CellMetrics(int width, int height)
        {
            Width = width;
            Height = height;
        }

        public CellMetrics(Size size)
            : this(size.Width, size.Height)
        {
        }

        public bool GetCell(Point location, out int cell)
        {
            cell = (location.Y * Width) + location.X;
            return Contains(location);
        }

        public bool GetLocation(int cell, out Point location)
        {
            location = new Point(cell % Width, cell / Width);
            return Contains(cell);
        }

        public bool Adjacent(Point location, FacingType facing, out Point adjacent)
        {
            adjacent = location;
            switch (facing)
            {
                case FacingType.North:
                    adjacent.Y--;
                    break;
                case FacingType.NorthEast:
                    adjacent.X++;
                    adjacent.Y--;
                    break;
                case FacingType.East:
                    adjacent.X++;
                    break;
                case FacingType.SouthEast:
                    adjacent.X++;
                    adjacent.Y++;
                    break;
                case FacingType.South:
                    adjacent.Y++;
                    break;
                case FacingType.SouthWest:
                    adjacent.X--;
                    adjacent.Y++;
                    break;
                case FacingType.West:
                    adjacent.X--;
                    break;
                case FacingType.NorthWest:
                    adjacent.X--;
                    adjacent.Y--;
                    break;
            }

            return Contains(adjacent);
        }

        public bool Adjacent(int cell, FacingType facing, out int adjacent)
        {
            if (!GetLocation(cell, out Point location) || !Adjacent(location, facing, out Point adjacentPoint))
            {
                adjacent = -1;
                return false;
            }
            else
            {
                return GetCell(adjacentPoint, out adjacent);
            }
        }

        public void Clip(ref Point location)
        {
            location.X = Math.Max(0, Math.Min(Width - 1, location.X));
            location.Y = Math.Max(0, Math.Min(Height - 1, location.Y));
        }

        public void Clip(ref Point location, Size margin)
        {
            Clip(ref location, margin, margin);
        }

        public void Clip(ref Point location, Size topLeftMargin, Size bottomRightMargin)
        {
            location.X = Math.Max(topLeftMargin.Width, Math.Min(Width - bottomRightMargin.Width - 1, location.X));
            location.Y = Math.Max(topLeftMargin.Height, Math.Min(Height - bottomRightMargin.Height - 1, location.Y));
        }
    }
}