/*
This test demonstrates director classes when the types are classes.
Shown are virtual function calls which use classes passed by:
  - Value
  - Reference
  - Pointer
as both parameters and return values.
The test also demonstrates directors used with:
  - method overloading
  - default parameters
Note: Methods with default parameters that call up from C++ cannot call
the overloaded C# methods, see DefaultParms method.

Expected output if PrintDebug enabled:
------------ Start ------------
Base - Val(444.555)
Base - Ref(444.555)
Base - Ptr(444.555)
Base - ConstPtrRef(444.555)
Base - FullyOverloaded(int 10)
Base - FullyOverloaded(bool 1)
Base - SemiOverloaded(int -678)
Base - SemiOverloaded(bool 1)
Base - DefaultParms(10, 2.25)
Base - DefaultParms(10, 1.125)
--------------------------------
Derived - Val(444.555)
Derived - Ref(444.555)
Derived - Ptr(444.555)
Derived - ConstPtrRef(444.555)
Derived - FullyOverloaded(int 10)
Derived - FullyOverloaded(bool 1)
Derived - SemiOverloaded(int -678)
Base - SemiOverloaded(bool 1)
Derived - DefaultParms(10, 2.25)
Derived - DefaultParms(10, 1.125)
--------------------------------
CSharpDerived - Val(444.555)
CSharpDerived - Ref(444.555)
CSharpDerived - Ptr(444.555)
CSharpDerived - ConstPtrRef(444.555)
CSharpDerived - FullyOverloaded(int 10)
CSharpDerived - FullyOverloaded(bool True)
CSharpDerived - SemiOverloaded(-678)
Base - SemiOverloaded(bool 1)
CSharpDerived - DefaultParms(10, 2.25)
CSharpDerived - DefaultParms(10, 1.125)
------------ Finish ------------
*/

using System;

namespace director_classesNamespace {

public class runme
{
  static void Main() 
  {
    runme r = new runme();
    r.run();
  }

  void run()
  {
    if (director_classes.PrintDebug) Console.WriteLine("------------ Start ------------");

    Caller myCaller = new Caller();

    // test C++ base class
    using (Base myBase = new Base(100.0))
    {
      makeCalls(myCaller, myBase);
    }

    if (director_classes.PrintDebug) Console.WriteLine("--------------------------------");

    // test vanilla C++ wrapped derived class
    using (Base myBase = new Derived(200.0))
    {
      makeCalls(myCaller, myBase);
    }

    if (director_classes.PrintDebug) Console.WriteLine("--------------------------------");

    // test director / C# derived class
    using (Base myBase = new CSharpDerived(300.0))
    {
      makeCalls(myCaller, myBase);
    }

    if (director_classes.PrintDebug) Console.WriteLine("------------ Finish ------------");
  }

  void makeCalls(Caller myCaller, Base myBase)
  {
    string NAMESPACE = "director_classesNamespace.";
    myCaller.set(myBase);

    DoubleHolder dh = new DoubleHolder(444.555);

    // Class pointer, reference and pass by value tests
    if (myCaller.ValCall(dh).val != dh.val) throw new Exception("failed");
    if (myCaller.RefCall(dh).val != dh.val) throw new Exception("failed");
    if (myCaller.PtrCall(dh).val != dh.val) throw new Exception("failed");
    if (myCaller.ConstPtrRefCall(dh).val != dh.val) throw new Exception("failed");

    // Fully overloaded method test (all methods in base class are overloaded)
    if (NAMESPACE + myCaller.FullyOverloadedCall(10) != myBase.GetType() + "::FullyOverloaded(int)") throw new Exception("failed");
    if (NAMESPACE + myCaller.FullyOverloadedCall(true) != myBase.GetType() + "::FullyOverloaded(bool)") throw new Exception("failed");

    // Semi overloaded method test (some methods in base class are overloaded)
    if (NAMESPACE + myCaller.SemiOverloadedCall(-678) != myBase.GetType() + "::SemiOverloaded(int)") throw new Exception("failed");
    if (myCaller.SemiOverloadedCall(true) != "Base" + "::SemiOverloaded(bool)") throw new Exception("failed");

    // Default parameters methods test
    if (NAMESPACE + myCaller.DefaultParmsCall(10, 2.25) != myBase.GetType() + "::DefaultParms(int, double)") throw new Exception("failed");
    if (myBase.GetType() == typeof(CSharpDerived)) { // special handling for C# derived classes, there is no way to do this any other way
      if (NAMESPACE + myCaller.DefaultParmsCall(10) != myBase.GetType() + "::DefaultParms(int, double)") throw new Exception("failed");
    } else {
      if (NAMESPACE + myCaller.DefaultParmsCall(10) != myBase.GetType() + "::DefaultParms(int)") throw new Exception("failed");
    }

    myCaller.reset();
  }
}

public class CSharpDerived : Base
{
  public CSharpDerived(double dd)
    : base(dd)
  {
  }

  public override DoubleHolder Val(DoubleHolder x)
  {
    if (director_classes.PrintDebug) Console.WriteLine("CSharpDerived - Val({0})", x.val);
    return x;
  }
  public override DoubleHolder Ref(DoubleHolder x)
  {
    if (director_classes.PrintDebug) Console.WriteLine("CSharpDerived - Ref({0})", x.val);
    return x;
  }
  public override DoubleHolder Ptr(DoubleHolder x)
  {
    if (director_classes.PrintDebug) Console.WriteLine("CSharpDerived - Ptr({0})", x.val);
    return x;
  }
  public override DoubleHolder ConstPtrRef(DoubleHolder x)
  {
    if (director_classes.PrintDebug) Console.WriteLine("CSharpDerived - ConstPtrRef({0})", x.val);
    return x;
  }
  public override String FullyOverloaded(int x)
  {
    if (director_classes.PrintDebug) Console.WriteLine("CSharpDerived - FullyOverloaded(int {0})", x);
    return "CSharpDerived::FullyOverloaded(int)";
  }
  public override String FullyOverloaded(bool x)
  {
    if (director_classes.PrintDebug) Console.WriteLine("CSharpDerived - FullyOverloaded(bool {0})", x);
    return "CSharpDerived::FullyOverloaded(bool)";
  }
  // Note no SemiOverloaded(bool x) method
  public override String SemiOverloaded(int x)
  {
    String ret = "CSharpDerived::SemiOverloaded(int)";
    if (director_classes.PrintDebug) Console.WriteLine("CSharpDerived - SemiOverloaded({0})", x);
    return ret;
  }
  public override String DefaultParms(int x, double y)
  {
    String ret = "CSharpDerived::DefaultParms(int, double)";
    if (director_classes.PrintDebug) Console.WriteLine("CSharpDerived - DefaultParms({0}, {1})", x, y);
    return ret;
  }
  // Note the following method can never be called from unmanaged code.
  // It is here only for code that calls it directly from managed code.
  // But should always be defined to ensure behaviour is consistent
  // independent of where DefaultParams is called from (managed or unmanaged code).
  // Note this method can never be called from unmanaged code
  public override String DefaultParms(int x)
  {
    if (director_classes.PrintDebug) Console.WriteLine("CSharpDerived - DefaultParms({0})", x);
    return DefaultParms(x, 1.125/*use C++ default here*/);
  }
}

}
