import * as FS from "fs";
import * as Path from "path";
import {
   EnumDefinition,
   IR,
   ServiceDefinition,
   Step,
   TypeDefinition,
} from "./ir";

export abstract class CompileTarget {
   abstract name: string;
   constructor(private outputFolder: string) {
      if (!FS.existsSync(outputFolder)) {
         FS.mkdirSync(outputFolder, {
            recursive: true,
         });
      }
   }

   abstract start(): void;

   abstract generateType(definition: TypeDefinition): void;

   abstract generateEnum(definition: EnumDefinition): void;

   abstract generateService(definition: ServiceDefinition): void;

   abstract finalize(steps: Step[]): void;

   protected writeFile(name: string, content: string | Promise<string>) {
      if (content instanceof Promise) {
         content.then((res) =>
            FS.writeFileSync(Path.join(this.outputFolder, name), res)
         );
      } else {
         FS.writeFileSync(Path.join(this.outputFolder, name), content);
      }
   }

   protected getTemplate(name: string): string {
      let path = Path.join(__dirname, "../templates/" + name);
      let file = FS.readFileSync(path, "utf-8");

      const splitted = file.split("\n");
      let res = [];
      let ignore = false;
      for (const line of splitted) {
         if (ignore) {
            ignore = false;
         } else if (line.trim().startsWith("//@template-ignore")) {
            ignore = true;
         } else {
            res.push(line);
         }
      }

      return res.join("\n");
   }
}

export default function compile(ir: IR, target: CompileTarget) {
   // Types are verified. They are now ready to be compiled to targets

   // setState("Building for target: " + target.name);
   target.start();
   ir.forEach((step) => {
      const [type, def] = step;
      if (type == "type") target.generateType(def as TypeDefinition);
      else if (type == "enum") target.generateEnum(def as EnumDefinition);
      else if (type == "service")
         target.generateService(def as ServiceDefinition);
   });
   if (target.finalize) target.finalize(ir);
}
