//>>> _using
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharpDX;
using SharpDX.Direct3D11;
using SharpDX.Windows;
//<<< _using

using Newtonsoft.Json;

namespace Framefield.Core.ID98656331_a3c2_4ad2_9329_39864ce29676
{
    public class Class_Game0815 : OperatorPart.Function, Framefield.Core.OperatorPartTraits.ITimeAccessor
    {

        OperatorPart.CollectOpPartFunctionsOfType<Framefield.Core.OperatorPartTraits.IVariableAccessor> _collector = new OperatorPart.CollectOpPartFunctionsOfType<Framefield.Core.OperatorPartTraits.IVariableAccessor>();
        List<OperatorPart.Function> _variableAccessorOpPartFunctions = new List<OperatorPart.Function>();

        const int PLAYFIELD_WIDTH = 20;
        const int PLAYFIELD_HEIGHT = 10;
        const int ACTION_TURN_LEFT = 1;
        const int ACTION_TURN_RIGHT = 2;

        public double FrameDuration = 0.16;

        int[] PlayField = new int[ PLAYFIELD_WIDTH * PLAYFIELD_HEIGHT ];

        public class Player
        {
            int[] _playField=null;

            public Player(int[] playField) {
                _playField = playField;
            }

            public String PlayerID { get; set; }
            public float PosX {  get { return (float)(PlayerFieldPosX + Math.Sin(Rotate) * (TraceLength % 1)); } }
            public float PosY { get { return (float)(PlayerFieldPosY - Math.Cos(Rotate) * (TraceLength % 1)); } }

            public int PlayerFieldPosX { get; set; }
            public int PlayerFieldPosY { get; set; }
            public float Rotate { get; set; } // in Degrees
            public double LastTime { get; set; }   // eventuelly used to filter idle players
            public double Speed { get; set; }
            public double TraceLength { get; set; }
            public int NextAction { get; set; }
            public int PlayerIndex { get; set; }
            public double BirthTime {get;set;}
            public float ColorR {get;set;}
            public float ColorG {get;set;}
            public float ColorB {get;set;}
            

            private void MoveRasterStet() {
                _playField[PlayerFieldPosX + PlayerFieldPosY * PLAYFIELD_WIDTH] = PlayerIndex;
                PlayerFieldPosX += (int)Math.Round(Math.Sin(Rotate));
                PlayerFieldPosY -= (int)Math.Round(Math.Cos(Rotate));

                if (PlayerFieldPosX < 0) {
                    PlayerFieldPosX += PLAYFIELD_WIDTH;
                }
                else if (PlayerFieldPosX >= PLAYFIELD_WIDTH) {
                    PlayerFieldPosX -= PLAYFIELD_WIDTH;
                }
                if (PlayerFieldPosY < 0) {
                    PlayerFieldPosY += PLAYFIELD_HEIGHT;
                }
                else if (PlayerFieldPosY >= PLAYFIELD_HEIGHT) {
                    PlayerFieldPosY -= PLAYFIELD_HEIGHT;
                }
            }

            public void Move( double frameDuration ) {
                double dt = frameDuration * Speed;
                // Reached turning point
                double rasterStep= ( TraceLength % 1) + dt;
                TraceLength += dt;
                if (rasterStep >= 1.0) {
                    while (rasterStep > 1) {
                        MoveRasterStet();
                        rasterStep -= 1;
                    }

                    if (NextAction == ACTION_TURN_LEFT) {
                        Rotate += (float)(0.5 * Math.PI);
                    }
                    else if (NextAction == ACTION_TURN_RIGHT) {
                        Rotate -= (float)(0.5 * Math.PI);
                    }
                    NextAction = 0;

                }               
            }
        }


        Dictionary<String, Player> _players = new Dictionary<string, Player>();

        //>>> _inputids
        private enum InputId
        {
            Input = 0,
            PlayerActions = 1
        }
        //<<< _inputids


        public class PlayerMessage
        {
            public string Timestamp { get; set; }
            public string Player { get; set; }
            public string Action { get; set; }
        }

        private Random _random = new Random();

        public override OperatorPartContext Eval(OperatorPartContext context, List<OperatorPart> inputs, int outputIdx) 
        {
            double dt = context.Time - _lastTime;
            _lastTime = context.Time;

            //>>> _params
            var Input = inputs[(int)InputId.Input];
            var PlayerActions = inputs[(int)InputId.PlayerActions].Eval(context).Text;
            //<<< _params

            string[] actions = PlayerActions.Split(new[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
            foreach (var action in actions) {
                PlayerMessage playerMessage = null;
                try {
                    playerMessage = JsonConvert.DeserializeObject<PlayerMessage>(action);
                }
                catch (Exception) {
                    Logger.Info(this,"Error deserializing message \"{0}\" to PlayerMessage!", action);
                    continue;
                }
                if (playerMessage != null) {
                    Logger.Info(this,"player {0} {1}s!", playerMessage.Player, playerMessage.Action);
                        Logger.Info(this,"here");
                    
                                            //Logger.Info(this,"here: {0}", playerMessage.Action);

                    if( playerMessage.Action == "JOIN_GAME" || !_players.ContainsKey(playerMessage.Player) ) {
                        if( _players.ContainsKey(playerMessage.Player) )
                            _players.Remove( playerMessage.Player );
                        var newPlayer= new Player(PlayField) { 
                            PlayerFieldPosX = 0, 
                            PlayerFieldPosY = 0, 
                            Rotate = 0, 
                            Speed = 1, 
                            PlayerID = playerMessage.Player,
                            BirthTime = context.Time,
                            ColorR = (float)_random.NextDouble(),
                            ColorG = (float)_random.NextDouble(),
                            ColorB = (float)_random.NextDouble(),
                        };
                        _players.Add(playerMessage.Player, newPlayer);
                    }
                    else if (playerMessage.Action == "EXIT") {
                        _players.Remove( playerMessage.Player );
                    }
                    
                    else if (playerMessage.Action == "TURN_LEFT") {
                        _players[playerMessage.Player].NextAction = ACTION_TURN_LEFT;                        
                    }
                    else if (playerMessage.Action == "TURN_RIGHT") {
                        _players[playerMessage.Player].NextAction = ACTION_TURN_RIGHT;
                    }
                }                
            }


            //collect all ops within the Subtree that access the Counter variable
            _collector.Clear();
            Input.TraverseWithFunction(_collector, null);
            _variableAccessorOpPartFunctions.Clear();

            foreach (var possibleOpPartFunction in _collector.CollectedOpPartFunctions) {
                _variableAccessorOpPartFunctions.Add(possibleOpPartFunction as OperatorPart.Function);
            }

            int playerIndex=0;
            foreach(var p in _players.Values) {            
                p.Move(dt);

                foreach (var opPartFunc in _variableAccessorOpPartFunctions) {
                    opPartFunc.OperatorPart.EmitChangedEvent();
                }
                context.Variables["PLAYER_LIFETIME"] = (float)(context.Time - p.BirthTime);
                //Logger.Info(this,"{0}", (float)(context.Time - p.BirthTime));
                context.Variables["PLAYER_X"] = p.PosX;
                context.Variables["PLAYER_Y"] = p.PosY;
                context.Variables["PLAYER_ROTATE"] = (float)(p.Rotate * 360.0 / Math.PI / 2.0);
                context.Variables["PLAYER_R"] = p.ColorR;
                context.Variables["PLAYER_G"] = p.ColorG;
                context.Variables["PLAYER_B"] = p.ColorB;
                context.Variables["PLAYER_INDEX"] = playerIndex++;
                Input.Eval(context);
            }

            return context;

        }
        private double _lastTime = 0;

    }
}

