using UnityEngine;
using System.Linq;
using System;

public static class PlayerLoader
{
    public static Player LoadPlayer(PlayerData data, StarMapGenerator generator) {
        if (!data.IsValid()) {
            Debug.LogError("load player error - data is invalid");
            return null;
        }
        var equipmentBlueprints = generator.GetEquipmentList();
        var economy = new EconomyParameters(
            generator.ResourceCost,
            equipmentBlueprints,
            generator.GetModifiersParams().Values.ToArray(),
            generator.FuelCost);
        var stantionTypes = data.StarSystem.StantionTypes.Select(type => new SpaceStantionType(type.Id, type.StockSize, type.Items, economy)).ToArray();
        var navPoints = LoadNavPoints(data.StarSystem.NavigationPoints, stantionTypes);
        var planets = LoadPlanets(data.StarSystem.Planets);
        var equip = data.StarShip.Equipment;
        var shipEquip = new ShipEquipment(equip.ShipId, equip.DriveId, equip.TankId, equip.CargoModuleId, equip.ShieldId, equip.WeaponId, equipmentBlueprints);
        const int playerFactionId = 1;
        var ship = new StarShip(
            data.StarShip.Position,
            data.StarShip.Rotation,
            playerFactionId,
            shipEquip,
            data.StarShip.Armor,
            data.StarShip.HyperFuel,
            data.StarShip.Items);
        return new Player(
            data.Name, data.Turn,
            ship,
            navPoints[data.CurrentNavigationPointId],
            navPoints,
            planets,
            stantionTypes,
            generator.GameParameters,
            economy,
            data.Credits,
            data.LastCosts);
    }

    private static Planet[] LoadPlanets(PlanetData[] planets)
    {
        if (planets == null)
            return Array.Empty<Planet>();
        return planets.Where(planetData => planetData != null).Select(data => new Planet(data.Position, data.Radius, data.Color)).ToArray();
    }

    public static PlayerData SavePlayer(Player player)
    {
        if (player == null)
            return null;


        return new PlayerData()
        {
            Name = player.Name,
            Turn = player.Turn,
            Credits = player.Credits,
            LastCosts = player.LastCost,
            CurrentNavigationPointId = player.CurrentPoint.Id,
            StarShip = ToStarShipData(player.StarShip),
            StarSystem = new StarSystemData()
            {
                StantionTypes = player.StantionTypes.Select(ToStantionTypeData).ToArray(),
                NavigationPoints = player.NavigationPoints.Select(ToNavigationPointData).ToArray(),
                Planets = player.Planets.Select(ToPlanetData).ToArray()
            }
        };
    }

    private static PlanetData ToPlanetData(Planet planet) => new PlanetData() { Position = planet.SystemPosition, Radius = planet.Radius, Color = planet.Color };

    private static SpaceStantionTypeData ToStantionTypeData(SpaceStantionType stantion)
    {
        return new SpaceStantionTypeData() {
            Id = stantion.Id,
            StockSize = stantion.Stock,
            Items = stantion.Consumption
        };
    }

    private static NavigationPoint[] LoadNavPoints(NavigationPointData[] navigationPointsData, SpaceStantionType[] stantionTypes)
    {
        var points = new NavigationPoint[navigationPointsData.Length];
        for (int nodeId = 0; nodeId < points.Length; nodeId++) {
            var data = navigationPointsData[nodeId];
            points[nodeId] = new NavigationPoint(nodeId, data.FactionId, data.Position, LoadStantions(nodeId, data.SpaceStantions, stantionTypes));
        }
        return points;
    }

    private static SpaceStantion[] LoadStantions(int nodeId, SpaceStantionData[] stantionsData, SpaceStantionType[] stantionTypes) 
    {
        var counter = 0;
        return stantionsData.Select(stantionData => ToSpaceStantionData(nodeId * 10 + (++counter), stantionData, stantionTypes.First(type => type.Id == stantionData.Id))).ToArray();
    }

    private static SpaceStantion ToSpaceStantionData(int id, SpaceStantionData data, SpaceStantionType type) =>
        new SpaceStantion(
            id,
            data.Position,
            data.Rotation,
            data.Name,
            type);

    private static StarShipData ToStarShipData(StarShip starShip) => new StarShipData()
    {
        Position = starShip.Position,
        Rotation = starShip.Rotation,
        HyperFuel = starShip.Fuel,
        Armor = starShip.Armor,
        Equipment = new EquipmentData() {
            ShipId = starShip.Equipment.Ship.Id,
            DriveId = starShip.Equipment.Drive.Id,
            TankId = starShip.Equipment.Tank.Id,
            CargoModuleId = starShip.Equipment.CargoModule.Id,
            ShieldId = starShip.Equipment.Shield.Id,
            WeaponId = starShip.Equipment.Weapon.Id,
        },
        Items = starShip.Resources.Items
    };

    private static NavigationPointData ToNavigationPointData(NavigationPoint point) => new NavigationPointData()
    {
        Position = point.SystemPostition,
        SpaceStantions = point.Stantions.Select(ToSpaceStantionData).ToArray(),
        FactionId = point.FactionId
    };

    private static SpaceStantionData ToSpaceStantionData(SpaceStantion stantion) => new SpaceStantionData()
    {
        Position = stantion.Position,
        Rotation = stantion.Rotation,
        Id = stantion.TypeId,
        Name = stantion.Name
    };

    private static bool IsValid(this PlayerData data)
    {
        if (data == null)
            return false;
        // check valid save data
        return true;
    }
}
