using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Sharpility.Extensions;
using NodaTime;
{{#imports}}using {{import}};
{{/imports}}

{{#models}}
{{#model}}
namespace {{packageName}}.{{packageContext}}.Models
{
    /// <summary>
    /// {{#description}}{{.}}{{/description}}{{^description}}{{classname}}{{/description}}
    /// </summary>
    public {{^hasChildren}}sealed {{/hasChildren}}class {{classname}}: {{#parent}}{{{parent}}}, {{/parent}} IEquatable<{{classname}}>
    { {{#vars}}{{^isInherited}}
        /// <summary>
        /// {{^description}}{{{name}}}{{/description}}{{#description}}{{description}}{{/description}}
        /// </summary>
        public {{>nullableDataType}} {{name}} { get; private set; }
{{/isInherited}}{{/vars}}

        /// <summary>
        /// Empty constructor required by some serializers.
        /// Use {{classname}}.Builder() for instance creation instead.
        /// </summary>
        [Obsolete]
        public {{classname}}(){{#parent}} : base({{/parent}}{{#parentVars}}null{{#hasMore}}, {{/hasMore}}{{/parentVars}}{{#parent}}){{/parent}}
        {
        }

        {{#hasChildren}}protected{{/hasChildren}}{{^hasChildren}}private{{/hasChildren}} {{classname}}({{#vars}}{{>nullableDataType}} {{name}}{{#hasMore}}, {{/hasMore}}{{/vars}}){{#parent}} : base({{#parentVars}}{{name}}{{#hasMore}}, {{/hasMore}}{{/parentVars}}){{/parent}}
        {
            {{#vars}}{{^isInherited}}
            this.{{name}} = {{name}};
            {{/isInherited}}{{/vars}}
        }

        /// <summary>
        /// Returns builder of {{classname}}.
        /// </summary>
        /// <returns>{{classname}}Builder</returns>
        public static {{#parent}}new {{/parent}}{{classname}}Builder Builder()
        {
            return new {{classname}}Builder();
        }

        /// <summary>
        /// Returns {{classname}}Builder with properties set.
        /// Use it to change properties.
        /// </summary>
        /// <returns>{{classname}}Builder</returns>
        public {{#parent}}new {{/parent}}{{classname}}Builder With()
        {
            return Builder()
            {{#vars}}
                .{{name}}({{name}}){{#hasMore}}
{{/hasMore}}{{/vars}};
        }

        public override string ToString()
        {
            return this.PropertiesToString();
        }

        public override bool Equals(object obj)
        {
            return this.EqualsByProperties(obj);
        }

        public bool Equals({{classname}} other)
        {
            return Equals((object) other);
        }

        public override int GetHashCode()
        {
            return this.PropertiesHash();
        }

        /// <summary>
        /// Implementation of == operator for ({{classname}}.
        /// </summary>
        /// <param name="left">Compared ({{classname}}</param>
        /// <param name="right">Compared ({{classname}}</param>
        /// <returns>true if compared items are equals, false otherwise</returns>
        public static bool operator == ({{classname}} left, {{classname}} right)
        {
            return Equals(left, right);
        }

        /// <summary>
        /// Implementation of != operator for ({{classname}}.
        /// </summary>
        /// <param name="left">Compared ({{classname}}</param>
        /// <param name="right">Compared ({{classname}}</param>
        /// <returns>true if compared items are not equals, false otherwise</returns>
        public static bool operator != ({{classname}} left, {{classname}} right)
        {
            return !Equals(left, right);
        }

        /// <summary>
        /// Builder of {{classname}}.
        /// </summary>
        public sealed class {{classname}}Builder
        {
            {{#vars}}
            private {{>nullableDataType}} _{{name}};
            {{/vars}}

            internal {{classname}}Builder()
            {
                SetupDefaults();
            }

            private void SetupDefaults()
            {
                {{#vars}}
                {{^required}}
                {{#defaultValue}}
                _{{name}} = {{{defaultValue}}};
                {{/defaultValue}}
                {{/required}}
                {{/vars}}
            }

            {{#vars}}
            /// <summary>
            /// Sets value for {{classname}}.{{{name}}} property.
            /// </summary>
            /// <param name="value">{{^description}}{{{name}}}{{/description}}{{#description}}{{description}}{{/description}}</param>
            public {{classname}}Builder {{name}}({{>nullableDataType}} value)
            {
                _{{name}} = value;
                return this;
            }

            {{/vars}}

            /// <summary>
            /// Builds instance of {{classname}}.
            /// </summary>
            /// <returns>{{classname}}</returns>
            public {{classname}} Build()
            {
                Validate();
                return new {{classname}}(
                    {{#vars}}
                    {{name}}: _{{name}}{{#hasMore}},{{/hasMore}}
                    {{/vars}}
                );
            }

            private void Validate()
            { {{#vars}}{{#required}}
                if (_{{name}} == null)
                {
                    throw new ArgumentException("{{name}} is a required property for {{classname}} and cannot be null");
                } {{/required}}{{/vars}}
            }
        }

        {{#vars}}{{#isEnum}}{{^parent}}
        {{>innerModelEnum}}{{/parent}}{{/isEnum}}{{#items.isEnum}}
        {{#items}}{{>innerModelEnum}}{{/items}}{{/items.isEnum}}{{/vars}}
    }
{{/model}}
{{/models}}
}
