using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System;

namespace Logic {
    public sealed class Action {
        public enum Type {
            set,   // A = B
            add,   // A = A + B
            sub,   // A = A - B
            inc,   // A = A + 1
            dec,   // A = A - 1
            mult,  // A = A * B
            div,   // A = A % B
        }

        [JsonProperty("item")]
        public string item; // id of item - operand A

        [JsonProperty("value")]
        public string value; // id of item or number - operand B

        [JsonProperty("action")]
        [JsonConverter(typeof(StringEnumConverter))]
        public Type operation;

        public Action() {}

        public Action(Type operation, string item, string value = "") {
            this.operation = operation;
            this.item = item;
            this.value = value;
        }

        public Action(Type operation, string item, int value) {
            this.operation = operation;
            this.item = item;
            this.value = value.ToString();
        }

        public void Process(Container container) {
            int operationValue = 0;
            if (!int.TryParse(value, out operationValue)) {
                operationValue = container[value];
            }
            switch (operation) {
                default:
                    throw new NotImplementedException();
                case Type.set:
                    container[item] = operationValue;
                    break;
                case Type.add:
                    container[item] = container[item] + operationValue;
                    break;
                case Type.sub:
                    container[item] = container[item] - operationValue;
                    break;
                case Type.inc:
                    container[item] = container[item] + 1;
                    break;
                case Type.dec:
                    container[item] = container[item] - 1;
                    break;
                case Type.mult:
                    container[item] = container[item] * operationValue;
                    break;
                case Type.div:
                    container[item] = container[item] / operationValue;
                    break;
            }
#if DEBUG_LOGIC
            UnityEngine.Debug.Log(string.Format("Process action [{0}] = {4} ({1} {2} {3})", item, operation, value, operationValue, container[item]));
#endif
        }

        internal string ToCompactString() {
            switch (operation) {
                default:
                    throw new NotImplementedException();
                case Type.set:
                    return String.Format("{0} = {1}", item, value);
                case Type.add:
                    return String.Format("{0} += {1}", item, value);
                case Type.sub:
                    return String.Format("{0} -= {1}", item, value);
                case Type.inc:
                    return String.Format("{0} ++", item);
                case Type.dec:
                    return String.Format("{0} --", item);
                case Type.mult:
                    return String.Format("{0} *= {1}", item, value);
                case Type.div:
                    return String.Format("{0} /= {1}", item, value);
            }
        }

        internal static Action ParseFromCompactString(string actionString) {
            const string Space = " ";
            var stricted = actionString.Replace(Space, String.Empty);
            Action action = null;
            if (TryParseAction(stricted, "+=", Type.add, out action)) {
                return action;
            }
            if (TryParseAction(stricted, "-=", Type.sub, out action)) {
                return action;
            }
            if (TryParseAction(stricted, "++", Type.inc, out action)) {
                return action;
            }
            if (TryParseAction(stricted, "--", Type.dec, out action)) {
                return action;
            }
            if (TryParseAction(stricted, "*=", Type.mult, out action)) {
                return action;
            }
            if (TryParseAction(stricted, "/=", Type.div, out action)) {
                return action;
            }
            if (TryParseAction(stricted, "=", Type.set, out action)) {
                return action;
            }
            return null;
        }

        private static bool TryParseAction(string stricted, string actionString, Type actionType, out Action action) {
            action = null;
            if (stricted.Contains(actionString)) {
                string[] values = stricted.Split(new string[] { actionString }, new StringSplitOptions());
                string item = values[0];
                string value = "";
                if (values.Length > 1) {
                    value = values[1];
                }
                if (values.Length >= 2) {
                    action = new Action(actionType, item, value);
                    return true;
                }
            }
            return false;
        }

    }
}
