using System;
using System.Collections.Generic;

namespace TrajectoryPlanningVisualization;

public sealed class VisualizationPolyline
{
    public VisualizationPolyline(string id, string legendChinese, string kind, VisualizationLineStyle lineStyle,
        IReadOnlyList<VisualizationPoint> points)
    {
        Id = VisualizationContract.RequireText(id, nameof(id));
        LegendChinese = VisualizationContract.RequireText(legendChinese, nameof(legendChinese));
        Kind = VisualizationContract.RequireText(kind, nameof(kind));
        LineStyle = lineStyle;
        Points = VisualizationContract.Copy(points, nameof(points));
    }

    public string Id { get; }
    public string LegendChinese { get; }
    public string Kind { get; }
    public VisualizationLineStyle LineStyle { get; }
    public IReadOnlyList<VisualizationPoint> Points { get; }
}

public sealed class VisualizationMarker
{
    public VisualizationMarker(string id, string kind, string labelChinese, VisualizationPoint position)
    {
        Id = VisualizationContract.RequireText(id, nameof(id));
        Kind = VisualizationContract.RequireText(kind, nameof(kind));
        LabelChinese = VisualizationContract.RequireText(labelChinese, nameof(labelChinese));
        Position = position ?? throw new ArgumentNullException(nameof(position));
    }

    public string Id { get; }
    public string Kind { get; }
    public string LabelChinese { get; }
    public VisualizationPoint Position { get; }
}

public sealed class VisualizationDirectionSegment
{
    public VisualizationDirectionSegment(int segmentIndex, string direction, bool startsAtGearSwitch,
        bool endsAtGearSwitch, IReadOnlyList<VisualizationPoint> points)
    {
        SegmentIndex = segmentIndex;
        Direction = VisualizationContract.RequireText(direction, nameof(direction));
        StartsAtGearSwitch = startsAtGearSwitch;
        EndsAtGearSwitch = endsAtGearSwitch;
        Points = VisualizationContract.Copy(points, nameof(points));
    }

    public int SegmentIndex { get; }
    public string Direction { get; }
    public bool StartsAtGearSwitch { get; }
    public bool EndsAtGearSwitch { get; }
    public IReadOnlyList<VisualizationPoint> Points { get; }
}

public sealed class VisualizationOccupancyGrid
{
    public VisualizationOccupancyGrid(VisualizationBounds bounds, double resolutionMeters, int rows, int columns,
        byte[] occupancyBits)
    {
        Bounds = bounds ?? throw new ArgumentNullException(nameof(bounds));
        VisualizationContract.RequireFinite(resolutionMeters, nameof(resolutionMeters));
        if (resolutionMeters <= 0d)
        {
            throw new ArgumentOutOfRangeException(nameof(resolutionMeters));
        }

        if (rows <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(rows));
        }

        if (columns <= 0)
        {
            throw new ArgumentOutOfRangeException(nameof(columns));
        }

        if (occupancyBits == null)
        {
            throw new ArgumentNullException(nameof(occupancyBits));
        }

        long cellCount = (long)rows * columns;
        long requiredByteCount = (cellCount + 7L) / 8L;
        if (occupancyBits.Length != requiredByteCount)
        {
            throw new ArgumentException("Occupancy bit count does not match the grid dimensions.", nameof(occupancyBits));
        }

        var bitsCopy = new byte[occupancyBits.Length];
        Buffer.BlockCopy(occupancyBits, 0, bitsCopy, 0, bitsCopy.Length);
        ResolutionMeters = resolutionMeters;
        Rows = rows;
        Columns = columns;
        OccupancyBitsBase64 = Convert.ToBase64String(bitsCopy);
    }

    public VisualizationBounds Bounds { get; }
    public double ResolutionMeters { get; }
    public int Rows { get; }
    public int Columns { get; }
    public string OccupancyBitsBase64 { get; }
}
