import { TypeDefinition, ServiceDefinition, EnumDefinition, Step } from "../ir";

import { CompileTarget } from "../compile";
import { LineAppender, lineAppender } from "../utils";
import chalk from "chalk";

const conversion = {
   boolean: "bool",
   int: "i64",
   float: "f64",
   string: "[]u8",
   void: "void",
   bytes: "[]u8",
};

function toZigType(type: string): string {
   return (conversion as any)[type] || type;
}

export class ZIGTarget extends CompileTarget<{  }> {
   name: string = "zig";

   start(): void {
      if (this.options.allow_bytes == true) {
         throw new Error("Zig has no support for 'bytes' yet!");
      }
   }

   getImport(name: string) {
      return `const ${name} = @import("./${name}.zig").${name};`;
   }

   generateImports(a: lineAppender, def: TypeDefinition | ServiceDefinition) {
      a(0, `const std = @import("std");`);
      def.depends.forEach((dep) => {
         a(0, this.getImport(dep));
      });
   }

   generateType(definition: TypeDefinition): void {
      const { a, getResult } = LineAppender();

      this.generateImports(a, definition);

      a(0, ``);

      a(0, `pub const ${definition.name} = struct {`);
      for (const field of definition.fields) {
         if (field.array) {
            a(1, `${field.name}: std.ArrayList(${toZigType(field.type)}),`);
         } else if (field.map) {
            a(
               1,
               `${field.name}: std.AutoHashMap(${toZigType(
                  field.map
               )}, ${toZigType(field.type)}),`
            );
         } else {
            a(1, `${field.name}: ${toZigType(field.type)},`);
         }
      }

      a(0, `};`);

      this.writeFile(`${definition.name}.zig`, getResult());
   }

   generateEnum(definition: EnumDefinition): void {
      const { a, getResult } = LineAppender();

      a(0, `pub const ${definition.name} = enum(i32) {`);
      for (const entry of definition.values) {
         a(1, `${entry.name} = ${entry.value},`);
      }
      a(0, `};`);

      this.writeFile(`${definition.name}.zig`, getResult());
   }

   generateService(definition: ServiceDefinition): void {
      console.log(
         chalk.yellow("[ZIG] WARNING:"),
         "ZIG support for services is not yet there. Service generation is skipped!"
      );
   }

   finalize(steps: Step[]): void {
      const { a, getResult } = LineAppender();

      steps.forEach(([type, def]) => {
         switch (type) {
            case "type":
               a(0, `pub ${this.getImport(def.name)}`);
               break;
            case "enum":
               a(0, `pub ${this.getImport(def.name)}`);
               break;
            default:
               console.warn(
                  chalk.yellow("[ZIG] WARNING:"),
                  "unimplemented step found:",
                  type
               );
            // case "service":
         }
      });

      this.writeFile(`mod.zig`, getResult());
   }
}
