import {
  WidgetModel,
  ISerializers,
  IBackboneModelOptions,
  {{ "unpack_models" if len(serializers_attrs)>0 else "" }}
} from '@jupyter-widgets/base';
import { MODULE_NAME, MODULE_VERSION } from '../version';
import { IUpdateManager } from '../types';
import { ObjectHash } from 'backbone';

export class {{class_name}}Model extends WidgetModel {
  defaults() {
    return {
      ...super.defaults(),
      _model_name: {{class_name}}Model.model_name,
      _model_module: {{class_name}}Model.model_module,
      _model_module_version: {{class_name}}Model.model_module_version{{ "," if len(defaults)>0 else "" }}
      {%- for default in defaults %}
      {{ default['name'] }}: undefined{{ "," if not loop.last else "" }}
      {%- endfor %}
    };
  }

  static serializers: ISerializers = {
    ...WidgetModel.serializers,
    {%- for attr in serializers_attrs %}
    {{attr}}: { deserialize: unpack_models as any }{{ "," if not loop.last else "" }}
    {%- endfor %}
  };

  toDict(): { [key: string]: any } {
    const dict: { [key: string]: any } = {};
    for (const [key, val] of Object.entries(this.attributes)) {
      if (key === 'unknown_attrs') {
        if (val) {
          for (const [unknown_key, unknown_val] of Object.entries(val)) {
            dict[unknown_key] = unknown_val;
          }
        }
        continue;
      }
      if (val !== undefined && val !== null && !key.startsWith('_')) {
        dict[key] = val;
      }
    }
    return dict;
  }

  initialize(attributes: ObjectHash, options: IBackboneModelOptions): void {
    super.initialize(attributes, options);
    if ({{class_name}}Model.updateManager) {
      {{class_name}}Model.updateManager.registerModel(this);
      this.comm?.on_close(() => {
        {{class_name}}Model.updateManager!.unregisterModel(this);
      });
    }
    this.listenTo(this, 'change', this.valueChanged);
  }

  valueChanged(m: {{class_name}}Model): void {
    if (m.changed) {
      Object.values(m.changed).forEach(it => {
        if (Array.isArray(it)) {
          it.forEach(subModel => {
            if (subModel?.model_id) {
              {{class_name}}Model.updateManager?.registerChildModel({
                child: subModel,
                parent: m
              });
            }
          });
        } else {
          if (it?.model_id) {
            {{class_name}}Model.updateManager?.registerChildModel({
              child: it,
              parent: m
            });
          }
        }
      });
    }

    if ({{class_name}}Model.updateManager) {
      {{class_name}}Model.updateManager.sendUpdateSignal(this);
      this.comm?.on_close(() => {
        {{class_name}}Model.updateManager!.unregisterModel(this);
      });
    }
  }

  static updateManager: IUpdateManager | null = null;
  static model_name = '{{model_name}}';
  static model_module = MODULE_NAME;
  static model_module_version = MODULE_VERSION;
}

