#include <triqs/parameters.hpp>
#include <iostream>

#include <vector>
#include <complex>
#include <string>

#include <boost/mpi.hpp>
#include <boost/mpi/environment.hpp>
#include <boost/mpi/communicator.hpp>
#include <boost/serialization/access.hpp>
#include <boost/serialization/complex.hpp>

#include <triqs/arrays.hpp>

using namespace triqs::utility;
namespace tqa=triqs::arrays;



void somefunc(parameters P){//pass by value
  std::cout << P["a"] << std::endl;
}


//useful to change _object to param or parameter
//and define the concept of a parameter
//this way we can explicitly define functions of
//parameters instead of native types

void somefunc(param p){//pass a single parameter by value
  std::cout << p << std::endl;
}



int main(int argc, char* argv[]) {
 mpi::environment env(argc, argv);
 mpi::communicator world;

 parameters P;

 if (mpi.rank() == 0) {

 P  ( "B", short(0), " short ")
    ( "C", unsigned short(1), " unsigned short ")
    ( "D", int(2), " int ")
    ( "E", unsigned int(3), " unsigned int ")
    ( "F", long(4), " long ")
    ( "G", long long(5), " long ")
    ( "H", float(6), " float ")
    ( "I", double(7.8), " doube ")
    ( "I", long double(9.10), " long double ")
    ( "J", std::complex<float>(11), " single complex ")
    ( "K", std::complex<double>(12), " double complex ")
    ( "L", std::string("13"), " string ")
    ( "M", std::vector<double> x{ 1,4 }, " vector ")
    ( "N", double(15))
    ( "W", int(16))
 ;

 P.set_default("U", std::string("U"))
              ("V", std::string("V"));

 P.set_default("W", std::string("W")); //warning: parameter has been set and will shadow default value

 P.set_default("X", std::string("X"));
 P.set_default("Y", std::string("Y"));
 P.set_default("Z", std::string("Z"), " Z ");

 std::cout << P["U"] << std::endl;

 if(!P.is_default("X") throw TRIQS_RUNTIME_ERROR << "parameter " << P["X"] << " not a default parameter" << std::endl;

 P["Y"] = 0.815; //must remove default status
 if(P.is_default("Y")) throw TRIQS_RUNTIME_ERROR << "parameter " << P["X"] << " is still a default parameter" << std::endl;

 }//rank=0

 mpi::broadcast(world,P,0);

 if (world.rank() == 0)
   std::cout << "rank 0" << std::endl << P << std::endl ;

 if (world.rank() == 1)
   std::cout << "rank 1" << std::endl << P << std::endl ;

 if (world.rank() == 0) {

 somefunc(P); //check that copying works (pass by value)

 param p(4.0); //declare a single parameter
 somefunc(p); //check that copying works (pass by value)

 param p2=P["A"]; //creation of a param from the type in P["A"]

 P["T"] = p2; //this should not register the type as param but as the type of P["A"]
 if(type_hash_code(P["T"]) != type_hash_code(P["A"])) throw TRIQS_RUNTIME_ERROR << "types don't match" << std::endl;

 param p3 = p2; //no call of any cast operator
 if(p2==p3) std::cout << "ok." << std::endl;
 if(p2!=p3) throw TRIQS_RUNTIME_ERROR << "param value mismatch" << std::endl;

 parameters P2(P);
 parameters P3; //new parameter list
 P3=P; //deep copy

 if(P2==P) std::cout << "ok." << std::endl;
 if(P2!=P) TRIQS_RUNTIME_ERROR << "parameter lists are not equal" << std::endl;
 if(P3!=P) TRIQS_RUNTIME_ERROR << "parameter lists are not equal" << std::endl;

 P2["ABC"]=5.0;
 if(P2==P) TRIQS_RUNTIME_ERROR << "parameter lists shouldn't be equal" << std::endl;

 tqa::array<double,2> A(2,2);
 P2["ArrayA"] = A;
 P2["ArrayB"] = tqa::array<double,2> B(3,3);
 P2["ArrayC"] = P2["ArrayA"] + P2["ArrayB"];

 } // rank==0

 return 0;
}