import { RJLog } from "../../library/RJLog";
import { GodotTypes } from "../GodotTypes";
import { MemberInitializer } from "../MemberInitializer";
import { MemberType } from "../MemberType";
import { Member } from "./Member";

export class Field extends Member
{
  static readonly Node = "Node";
  static readonly Resource = "Resource";

  constructor( memberInitializer:MemberInitializer )
  { 
    super( MemberType.Field, memberInitializer ); 
  }

  isReference = false;
  referenceType = "";
  isProperty = false;
  propertySignalName = "";
  propertyActionName = "";

  parseBody( body:any )
  {
    if ( typeof body === "boolean" )
    {
      this.type = "bool";
      this.initialValue = body + ""; 
      return;
    }

    if ( typeof body === "number" )
    {
      this.type = "float";
      this.initialValue = body + ""; 
      return;
    }

    let regex = /((?:\w|\<|\>)+)(?:\s*\=\s*(.+))?/;
    let result = regex.exec( body );
    let typeOrRefRegex = /^(Ref|Resource|Node)</;
    let refRegex = /^Ref<(\w+)>/;

    let typeResult = result[ 1 ]

    this.isReference = typeOrRefRegex.test( typeResult );

    // RJLog.log( "Is Reference", this.name, this.isReference, typeResult );

    if ( this.isReference )
    {
      this.referenceType = refRegex.test( typeResult ) ? refRegex.exec( typeResult )[ 1 ] :
                           typeOrRefRegex.exec( typeResult )[ 1 ];

      // RJLog.log( "referenceType", body, `"${this.referenceType}"`, typeResult );

      if ( Field.Node === this.referenceType )
      {
        typeResult = typeResult.replace( typeOrRefRegex, "" ).replace( />$/, "");
        typeResult = typeResult + "*";
      }
      else 
      {
        typeResult = typeResult.replace( typeOrRefRegex, "Ref<" );

        if ( result.length >= 2 )
        {
          this.initialValue = result[ 2 ];
        }
      }  

    }    
   
    this.type = typeResult;
    this.initialValue = result[ 2 ] || null;
    
  }

  getFieldDeclaration()
  {
    let fieldInfo = "\n  " + this.info() + "\n  ";

    if ( Field.Node === this.referenceType )
    {
      return `${fieldInfo}${this.type} ${this.name} = nullptr;`;
    }

    return `${fieldInfo}${this.type} ${this.name};`
  }

  getHeaderDefinition():string
  {
    let fieldInfo = "\n  " + this.info() + "\n  ";

    if ( Field.Node === this.referenceType )
    {
      return `${fieldInfo}${this.type} get_${this.name}() const; void set_${this.name}( ${this.type} p_${this.name} );`;
    }

    if ( Field.Resource === this.referenceType )
    {
      return `${fieldInfo}${this.type} get_${this.name}() const; void set_${this.name}( const ${this.type} &p_${this.name} );`;
    }

    return `${fieldInfo}${this.type} get_${this.name}(); void set_${this.name}( ${this.type} p_${this.name} );`
  }

  getBindings( className:string ):string
  {   
    

    let bindings = [];

    bindings.push( `// ${this.name}: ${this.type}` );

    bindings.push( `ClassDB::bind_method( D_METHOD( "set_${this.name}", "${this.name}" ), &${className}::set_${this.name} );` );
    bindings.push( `ClassDB::bind_method( D_METHOD( "get_${this.name}"), &${className}::get_${this.name});` );    


    if ( this.isReference )
    {
      // ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "reference_property", PROPERTY_HINT_NODE_TYPE), "set_reference_property", "get_reference_property");
       // RJLog.log( "Searching propertyHint:", this.name, this.type, this.referenceType );
      let propertyHint = GodotTypes.stringToPropertyHint( this.referenceType );      

      bindings.push( `ADD_PROPERTY( PropertyInfo(Variant::OBJECT, "${this.name}", ${propertyHint} ), "set_${this.name}", "get_${this.name}" );` );  

    }
    else
    {   
      let type = GodotTypes.stringToVariantType( this.type );      
      bindings.push( `ADD_PROPERTY(PropertyInfo( Variant::${type}, "${this.name}" ), "set_${this.name}", "get_${this.name}" );` );      
    }

    bindings.push( ` ` );    

    //ClassDB::bind_method(D_METHOD("set_color", "color"), &GLTFLight::set_color);
    //ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); // Color

    return bindings.join( "\n  " );

  }


  getFieldImplementation( className:string )
  {
    let implementations = [];

    implementations.push( `// ${this.name}: ${this.type}` );

    let eventCallbacks = "";

    if ( this.isProperty )
    {
      eventCallbacks += `\n`;
      eventCallbacks += `\n  emit_signal( SNAME( "${this.propertySignalName}" ) );`;
      eventCallbacks += `\n`;
      eventCallbacks += `\n  if ( ${this.propertyActionName} != NULL )`;
      eventCallbacks += `\n  {`;
      eventCallbacks += `\n    ${this.propertyActionName}->trigger();`;
      eventCallbacks += `\n  }`;
    }

    let n = this.isProperty ? "\n" : "";
    let n2 = this.isProperty ? "\n " : "";

    if ( Field.Node === this.referenceType )
    {
      implementations.push( `${this.type} ${className}::get_${this.name}() const { return ${this.name}; }` );
      implementations.push( `void ${className}::set_${this.name}( ${this.type} p_${this.name} ) ${n}{${n2} ${this.name} = p_${this.name};${eventCallbacks} ${n}}` );
    }
    else if ( Field.Resource === this.referenceType )
    {
      implementations.push( `${this.type} ${className}::get_${this.name}() const { return ${this.name}; }` );
      implementations.push( `void ${className}::set_${this.name}( const ${this.type} &p_${this.name} ) ${n}{${n2} ${this.name} = p_${this.name};${eventCallbacks} ${n}}` );
    }
    else
    {
      implementations.push( `${this.type} ${className}::get_${this.name}() { return ${this.name}; }` );
      implementations.push( `void ${className}::set_${this.name}( ${this.type} p_${this.name} ) ${n}{${n2} ${this.name} = p_${this.name};${eventCallbacks} ${n}}` );
    }
    
    implementations.push( ` ` );

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

  get hasInitializer()
  {
    return this.initialValue !== null;
  }

  getInitializer()
  {
    return `${this.name} = ${this.initialValue};`;
  }
}