// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Linq;
using Newtonsoft.Json.Linq;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Taiko;

namespace osu.Desktop.LegacyIpc
{
    /// <summary>
    /// Provides IPC to legacy osu! clients.
    /// </summary>
    public class LegacyTcpIpcProvider : TcpIpcProvider
    {
        private static readonly Logger logger = Logger.GetLogger("legacy-ipc");

        public LegacyTcpIpcProvider()
            : base(45357)
        {
            MessageReceived += msg =>
            {
                try
                {
                    logger.Add("Processing legacy IPC message...");
                    logger.Add($"    {msg.Value}", LogLevel.Debug);

                    // See explanation in LegacyIpcMessage for why this is done this way.
                    var legacyData = ((JObject)msg.Value).ToObject<LegacyIpcMessage.Data>();
                    object value = parseObject((JObject)legacyData!.MessageData, legacyData.MessageType);

                    return new LegacyIpcMessage
                    {
                        Value = onLegacyIpcMessageReceived(value)
                    };
                }
                catch (Exception ex)
                {
                    logger.Add($"Processing IPC message failed: {msg.Value}", exception: ex);
                    return null;
                }
            };
        }

        private object parseObject(JObject value, string type)
        {
            switch (type)
            {
                case nameof(LegacyIpcDifficultyCalculationRequest):
                    return value.ToObject<LegacyIpcDifficultyCalculationRequest>()
                           ?? throw new InvalidOperationException($"Failed to parse request {value}");

                case nameof(LegacyIpcDifficultyCalculationResponse):
                    return value.ToObject<LegacyIpcDifficultyCalculationResponse>()
                           ?? throw new InvalidOperationException($"Failed to parse request {value}");

                default:
                    throw new ArgumentException($"Unsupported object type {type}");
            }
        }

        private object onLegacyIpcMessageReceived(object message)
        {
            switch (message)
            {
                case LegacyIpcDifficultyCalculationRequest req:
                    try
                    {
                        WorkingBeatmap beatmap = new FlatWorkingBeatmap(req.BeatmapFile);
                        var ruleset = beatmap.BeatmapInfo.Ruleset.CreateInstance();
                        Mod[] mods = ruleset.ConvertFromLegacyMods((LegacyMods)req.Mods).ToArray();

                        return new LegacyIpcDifficultyCalculationResponse
                        {
                            StarRating = ruleset.CreateDifficultyCalculator(beatmap).Calculate(mods).StarRating
                        };
                    }
                    catch
                    {
                        return new LegacyIpcDifficultyCalculationResponse();
                    }

                default:
                    throw new ArgumentException($"Unsupported message type {message}");
            }
        }

        private static Ruleset getLegacyRulesetFromID(int rulesetId)
        {
            switch (rulesetId)
            {
                case 0:
                    return new OsuRuleset();

                case 1:
                    return new TaikoRuleset();

                case 2:
                    return new CatchRuleset();

                case 3:
                    return new ManiaRuleset();

                default:
                    throw new ArgumentException("Invalid ruleset id");
            }
        }
    }
}
