export type {{ident}} =
  {%- let type_variant = ident.clone() %}
  {%- for variant in variants -%}
    {%- match variant -%}
      {%- when EnumVariantDeclaration::Unit with { ident, value } %}
  | { tag: '{{ident}}' }
      {%- when EnumVariantDeclaration::Unnamed with { ident, types } %}
        {%- if types.len() == 1 %}
  | { tag: '{{ident}}'; value: {{ variant.first_type().ts_type() }} }
        {%- else %}
  | { tag: '{{ident}}'; value: {{type_variant}}_{{ident}} }
        {%- endif -%}
      {%- when EnumVariantDeclaration::Named with { ident, types } %}
  | { tag: '{{ident}}'; value: {{type_variant}}_{{ident}} }
    {%- endmatch -%}
  {% endfor -%} 
;
  {%- for variant in variants -%}
    {%- match variant -%}
      {%- when EnumVariantDeclaration::Unnamed with { ident, types } -%}
        {%- if types.len() > 1 %}  
export interface {{type_variant}}_{{ident}} {
          {%- for ty in types %}
  {{ loop.index - 1 }}: {{ ty.ts_type() }};
          {%- endfor %} 
}
        {% endif -%}
      {%- when EnumVariantDeclaration::Named with { ident, types } %}
export interface {{type_variant}}_{{ident}} {
          {%- for (id, ty) in types %}
  {{ id }}: {{ ty.ts_type() }};
          {%- endfor %}
}
      {% else %}
    {%- endmatch -%}
  {% endfor %} 

export module {{ident}} {
  {%- for variant in variants -%}
    {%- match variant -%}
      {%- when EnumVariantDeclaration::Unit with { ident, value } %}
  export const {{ident}}: {{type_variant}} = { tag: '{{ident}}' };
      {%- when EnumVariantDeclaration::Unnamed with { ident, types } %}
        {%- if types.len() == 1 %}
  export const {{ident}} = (value: {{ variant.first_type().ts_type() }}): {{type_variant}} => ({ tag: '{{ident}}', value });
        {%- else %}
  export const {{ident}} = (
          {%- for ty in types -%}
              p{{loop.index - 1}}: {{ ty.ts_type() }},
          {%- endfor -%}
  ): {{type_variant}} => ({ tag: '{{ident}}', value: [
          {%- for ty in types -%}
              p{{loop.index - 1}},
          {%- endfor -%}
  ] });
        {%- endif -%}
      {%- when EnumVariantDeclaration::Named with { ident, types } %}
  export const {{ident}} = (value: {{type_variant}}_{{ident}}): {{type_variant}} => ({ tag: '{{ident}}', value });
    {%- endmatch -%}
  {% endfor %} 
}

export function write{{ident}}(value: {{ident}}, sinkOrBuf?: SinkOrBuf): Sink {
  const sink = Sink.create(sinkOrBuf);
  switch(value.tag) {
{%- for variant in variants %}
  {%- let i = loop.index %}
    case '{{variant.ident()}}':
      writeU32({{loop.index - 1}}, sink);
  {%- match variant -%}
    {%- when EnumVariantDeclaration::Unit with { ident, value } %}
    {%- when EnumVariantDeclaration::Unnamed with { ident, types } %}
      {%- if types.len() == 1 %}
      const val{{i}} = value as { value: {{ variant.first_type().ts_type() }} };
      {{ variant.first_type().writer() }}(val{{i}}.value, sink);
      {%- else %}
      const val{{i}} = value as { value: {{type_variant}}_{{ident}} };
        {%- for ty in types %}
      {{ ty.writer() }}(val{{i}}.value[{{loop.index -1}}], sink);
        {%- endfor %} 
      {%- endif -%}
    {%- when EnumVariantDeclaration::Named with { ident, types } %}
      const val{{i}} = value as { value: {{type_variant}}_{{ident}} };
      {%- for (id, ty) in types %}
      {{ ty.writer() }}(val{{i}}.value.{{id}}, sink);
      {%- endfor -%}
    {%- endmatch %}
      break;
{%- endfor %} 
    default:
       throw new Error(`'${(value as any).tag}' is invalid tag for enum '{{ident}}'`);
  }

  return sink;
}

export function read{{ident}}(sinkOrBuf: SinkOrBuf): {{ident}} {
  const sink = Sink.create(sinkOrBuf);
  const value = readU32(sink);
  switch (value) {
{%- for variant in variants %}
    case {{loop.index - 1}}:
  {%- match variant -%}
    {%- when EnumVariantDeclaration::Unit with { ident, value } %} 
      return {{type_variant}}.{{ident}};
    {%- when EnumVariantDeclaration::Unnamed with { ident, types } %}
      return {{type_variant}}.{{ident}}(
      {%- for ty in types %}
        {{ ty.reader() }}(sink),
      {%- endfor %} 
      );
    {%- when EnumVariantDeclaration::Named with { ident, types } %}
      return {{type_variant}}.{{ident}}({
      {%- for (id, ty) in types %}
        {{ id }}: {{ ty.reader() }}(sink),
      {%- endfor %}
      });
  {%- endmatch -%}
{%- endfor %}
    default:
      throw new Error(`'${value}' is invalid value for enum '{{ident}}'`);
  }
}