#include <iostream>

#include <mpi.h>
#include <stdio.h>
#include "cca.h"
#include "ports/Function_CCA.h"

#include "ParallelIntegrator_CCA.h"
#include "macros_CCA.h"

namespace integrators
{
  namespace ccaimpl
  {

    // ParallelIntegrator implementation

    ParallelIntegrator::ParallelIntegrator ()
    {
      function_m = 0;
    }

    ParallelIntegrator::~ParallelIntegrator ()
    {
    }

    double
      ParallelIntegrator::integrate (double lowBound, double upBound,
                                     int count)
    {
      gov::cca::Port * port;

      // Get Function port
      port = frameworkServices->getPort ("FunctionPort");
      if (port)
        function_m = dynamic_cast < functions::ccaports::Function * >(port);
      if (function_m == 0)
        {
          cerr << "Connected to something other than a FunctionPort port" <<
            endl;
          return -1;
        }

      int n, myid, numprocs, i;
      double result, myresult, h, sum, x;
      int namelen;
      char processor_name[MPI_MAX_PROCESSOR_NAME];

      MPI_Comm_size (MPI_COMM_WORLD, &numprocs);
      MPI_Comm_rank (MPI_COMM_WORLD, &myid);
      MPI_Get_processor_name (processor_name, &namelen);

      fprintf (stderr, "Process %d on %s: number of intervals = %d\n", myid,
               processor_name, count);

      MPI_Bcast (&count, 1, MPI_INT, 0, MPI_COMM_WORLD);
      if (count == 0)
        {
          return -1;
        }
      else
        {
          h = (upBound - lowBound) / (double) count;
          sum = 0.0;
          for (i = myid + 1; i <= count; i += numprocs)
            {
              x = h * ((double) i - 0.5);
              sum += function_m->evaluate (x);
            }
          myresult = h * sum;

          MPI_Reduce (&myresult, &result, 1, MPI_DOUBLE, MPI_SUM, 0,
                      MPI_COMM_WORLD);

        }

      // Release ports
      frameworkServices->releasePort ("FunctionPort");

      printf ("result is %f\n", result);
      return result;
    }

    /**
     * The framework passes a pointer to the Services object
     * via this method, which is called by the framework as soon
     * as the component is instantiated.
     */
    void ParallelIntegrator::setServices (gov::cca::Services * services)
    {
      if (services != 0)
        {
          frameworkServices = services;

          // Provide an Integrator port
          gov::cca::PortInfo * portInfo =
            frameworkServices->createPortInfo ("IntegratorPort",
                                               "integrators.ccaports.Integrator",
                                               0);
          if (portInfo != 0)
            frameworkServices->addProvidesPort (this, portInfo);

          // Use a Function port
          portInfo =
            frameworkServices->createPortInfo ("FunctionPort",
                                               "functions.ccaports.Function",
                                               0);
          frameworkServices->registerUsesPort (portInfo);
        }
      else
        {
          // Close down if not closed already
          if (frameworkServices != 0)
            frameworkServices->removeProvidesPort ("IntegratorPort");
          frameworkServices = 0;
        }
    }

  }
}