/* types.vala
 *
 * Copyright 2020 Princeton Ferro <princetonferro@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

using Gee;

namespace Vls {
    class CompileCommand : Object, Json.Serializable {
        public string directory { get; set; }
        public string[] command { get; set; }
        public string file { get; set; }
        public string output { get; set; }

        public new void Json.Serializable.set_property (ParamSpec pspec, Value value) {
            base.set_property (pspec.get_name (), value);
        }

        public new Value Json.Serializable.get_property (ParamSpec pspec) {
            Value val = Value(pspec.value_type);
            base.get_property (pspec.get_name (), ref val);
            return val;
        }

        public unowned ParamSpec? find_property (string name) {
            return this.get_class ().find_property (name);
        }

        public Json.Node serialize_property (string property_name, Value value, ParamSpec pspec) {
            error ("MesonTarget: serialization not supported");
        }

        public bool deserialize_property (string property_name, out Value val, ParamSpec pspec, Json.Node property_node) {
            if (property_name == "directory" ||
                property_name == "file" ||
                property_name == "output") {
                val = Value (typeof (string));
                val.set_string (property_node.get_string ());
                return true;
            } else if (property_name == "command") {
                val = Value (typeof (string[]));
                string[] command_array = {};
                try {
                    string? command_str = property_node.get_string ();
                    if (command_str != null)
                        command_array = Util.get_arguments_from_command_str (property_node.get_string ());
                } catch (RegexError e) {
                    warning ("failed to parse `%s': %s", property_node.get_string (), e.message);
                }
                val.set_boxed (command_array);
                return true;
            }
            val = Value (pspec.value_type);
            return false;
        }
    }

    class Pair<T,U> : Object {
        public T first { get; private set; }
        public U second { get; private set; }

        public Pair (T first, U second) {
            this.first = first;
            this.second = second;
        }
    }
}

namespace Meson {
    class TargetSourceInfo : Object {
        public string language { get; set; }
        public string[] compiler { get; set; }
        public string[] parameters { get; set; }
        public string[] sources { get; set; }
        /**
         * Like sources, but comes from files generated by other targets.
         */
        public string[] generated_sources { get; set; }
    }

    class TargetInfo : Object, Json.Serializable {
        public string name { get; set; }
        public string id { get; set; }
        // Vala codegen forbids having 'type' as a property
        public string target_type { get; set; }
        public string defined_in { get; set; }
        public string?[] filename { get; set; }
        public ArrayList<TargetSourceInfo> target_sources { get; set; }

        public new void Json.Serializable.set_property (ParamSpec pspec, Value value) {
            base.set_property (pspec.get_name (), value);
        }

        public new Value Json.Serializable.get_property (ParamSpec pspec) {
            Value val = Value(pspec.value_type);
            base.get_property (pspec.get_name (), ref val);
            return val;
        }

        public unowned ParamSpec? find_property (string name) {
            if (name == "type")
                return this.get_class ().find_property ("target_type");
            return this.get_class ().find_property (name);
        }

        public Json.Node serialize_property (string property_name, Value value, ParamSpec pspec) {
            error ("MesonTarget: serialization not supported");
        }

        public bool deserialize_property (string property_name, out Value val, ParamSpec pspec, Json.Node property_node) {
            if (property_name == "name" ||
                property_name == "id" ||
                property_name == "type" ||
                property_name == "defined-in") {
                val = Value (typeof (string));
                val.set_string (property_node.get_string ());
                return true;
            } else if (property_name == "filename") {
                val = Value (typeof (string[]));
                var array = new string [property_node.get_array ().get_length ()];
                property_node.get_array ().foreach_element ((json_array, i, node) => {
                    array[i] = node.get_string ();
                });
                val.set_boxed (array);
                return true;
            } else if (property_name == "target-sources") {
                target_sources = new ArrayList<TargetSourceInfo> ();
                property_node.get_array ().foreach_element ((_1, _2, node) => {
                    Json.Node? language_property = node.get_object ().get_member ("language");
                    if (language_property == null)
                        return;
                    var tsi = Json.gobject_deserialize (typeof (Meson.TargetSourceInfo), node) as Meson.TargetSourceInfo?;
                    assert (tsi != null);
                    target_sources.add (tsi);
                });
                val = Value (target_sources.get_type ());
                val.set_object (target_sources);
                return true;
            }
            val = Value (pspec.value_type);
            return false;
        }
    }

    /**
     * can be found in `$BUILD_ROOT/meson-info/intro-dependencies.json`
     */
    class Dependency : Object {
        public string name { get; set; }
        public string version { get; set; }
        public string[] compile_args { get; set; }
        public string[] link_args { get; set; }
    }
}
