/* This is the close handler for a target-push delta stream.  It sends
 * a final window if there is any buffered target data, and then sends
 * a NULL window signifying the end of the window stream. */
static svn_error_t *
tpush_close_handler(void *baton)
{
  struct tpush_baton *tb = baton;
  svn_txdelta_window_t *window;

  /* Send a final window if we have any residual target data. */
  if (tb->target_len > 0)
    {
      window = compute_window(tb->buf, tb->source_len, tb->target_len,
                              tb->source_offset, tb->pool);
      SVN_ERR(tb->wh(window, tb->whb));
    }

  /* Send a final NULL window signifying the end. */
  return tb->wh(NULL, tb->whb);
}
/* This is the write handler for a target-push delta stream.  It reads
 * source data, buffers target data, and fires off delta windows when
 * the target data buffer is full. */
static svn_error_t *
tpush_write_handler(void *baton, const char *data, apr_size_t *len)
{
  struct tpush_baton *tb = baton;
  apr_size_t chunk_len, data_len = *len;
  apr_pool_t *pool = svn_pool_create(tb->pool);
  svn_txdelta_window_t *window;

  while (data_len > 0)
    {
      svn_pool_clear(pool);

      /* Make sure we're all full up on source data, if possible. */
      if (tb->source_len == 0 && !tb->source_done)
        {
          tb->source_len = SVN_DELTA_WINDOW_SIZE;
          SVN_ERR(svn_stream_read(tb->source, tb->buf, &tb->source_len));
          if (tb->source_len < SVN_DELTA_WINDOW_SIZE)
            tb->source_done = TRUE;
        }

      /* Copy in the target data, up to SVN_DELTA_WINDOW_SIZE. */
      chunk_len = SVN_DELTA_WINDOW_SIZE - tb->target_len;
      if (chunk_len > data_len)
        chunk_len = data_len;
      memcpy(tb->buf + tb->source_len + tb->target_len, data, chunk_len);
      data += chunk_len;
      data_len -= chunk_len;
      tb->target_len += chunk_len;

      /* If we're full of target data, compute and fire off a window. */
      if (tb->target_len == SVN_DELTA_WINDOW_SIZE)
        {
          window = compute_window(tb->buf, tb->source_len, tb->target_len,
                                  tb->source_offset, pool);
          SVN_ERR(tb->wh(window, tb->whb));
          tb->source_offset += tb->source_len;
          tb->source_len = 0;
          tb->target_len = 0;
        }
    }

  svn_pool_destroy(pool);
  return SVN_NO_ERROR;
}
static svn_error_t *
txdelta_next_window(svn_txdelta_window_t **window,
                    void *baton,
                    apr_pool_t *pool)
{
  struct txdelta_baton *b = baton;
  apr_size_t source_len = SVN_DELTA_WINDOW_SIZE;
  apr_size_t target_len = SVN_DELTA_WINDOW_SIZE;

  /* Read the source stream. */
  if (b->more_source)
    {
      SVN_ERR(svn_stream_read(b->source, b->buf, &source_len));
      b->more_source = (source_len == SVN_DELTA_WINDOW_SIZE);
    }
  else
    source_len = 0;

  /* Read the target stream. */
  SVN_ERR(svn_stream_read(b->target, b->buf + source_len, &target_len));
  b->pos += source_len;

  if (target_len == 0)
    {
      /* No target data?  We're done; return the final window. */
      if (b->context != NULL)
        SVN_ERR(svn_checksum_final(&b->checksum, b->context, b->result_pool));

      *window = NULL;
      b->more = FALSE;
      return SVN_NO_ERROR;
    }
  else if (b->context != NULL)
    SVN_ERR(svn_checksum_update(b->context, b->buf + source_len, target_len));

  *window = compute_window(b->buf, source_len, target_len,
                           b->pos - source_len, pool);

  /* That's it. */
  return SVN_NO_ERROR;
}
int main(int argc, char **argv)
{
        int ret;

        ret = MPI_Init(&argc, &argv);
        check(ret == MPI_SUCCESS, "Failed to init MPI");

        struct prog_options o;
        parse_options(&o, argc, argv);


        /* We will save the result to a file. Try to open it right ahead so
         * we don't compute 5 minutes before knowing we don't haveee the rights
         * on it... */
        FILE * outFile = fopen(o.outFileName, "w");
        check_null(outFile);

        /* Compute the steps */
        double stepX = (o.area.endX - o.area.startX) / (double)(o.width - 1);
        double stepY = (o.area.endY - o.area.startY) / (double)(o.height - 1);

        /* In a first time, check the number of lines is multiple of the number
         * of nodes. */
        int nbNodes, rank;
        ret = MPI_Comm_size(MPI_COMM_WORLD, &nbNodes);
        check (ret == MPI_SUCCESS, "Failed to get comm size");

        ret = MPI_Comm_rank(MPI_COMM_WORLD, &rank);
        check (ret == MPI_SUCCESS, "Failed to get comm rank");



        /****** Now starts the differentiation between the master node and others *****/

        debug("Node %d: handles %d lines from %d\n", rank,
              lines_at_node(rank, nbNodes, o.height), first_line_of_node(rank, nbNodes, o.height));
        
        /* Only the master node allocates the whole image, others allocate smaller ones */
        color_t *image = NULL;
        if (rank == 0) {
                image = allocate_image(o.width, o.height);
        } else {
                image = allocate_image(o.width, lines_at_node(rank, nbNodes, o.height));
        }

        /* Everybody computes his part of the image */
        struct complex_plan_area myArea = {
                o.area.startX, /* From the left... */
                o.area.startY + first_line_of_node(rank, nbNodes, o.height) * stepY, /* start of my slice */
                o.area.endX, /* ... to the right */
                o.area.startY + (first_line_of_node(rank + 1, nbNodes, o.height) - 1) * stepY,
        };

        ret = compute_window(image, myArea, o.width, lines_at_node(rank, nbNodes, o.height),
                             stepX, stepY, o.threshold, o.maxIter, classic_mandelbrot);
        check(ret == 0, "Failed to compute the image");


        /* Now, the master waits for other to send their results */
        MPI_Status status;
        if (rank == 0) {
                for (int node = 1; node < nbNodes; node++) {
                        debug("Receiving from node %d...", node);
                        int firstPixel = first_line_of_node(node, nbNodes, o.height) * o.width;
                        int nPixels = lines_at_node(node, nbNodes, o.height) * o.width;
                        ret = MPI_Recv(&image[firstPixel], nPixels, MPI_BYTE, node, MPI_ANY_TAG,
                                       MPI_COMM_WORLD, &status);
                        check(ret == MPI_SUCCESS, "Node 0: Failed to receive from node %d\n", node);
                }

                /* And save the result to the file */
                ret = save_image(image, o.width, o.height, outFile);
                check(ret == 0, "Failed to save the image to file");
        } else {
                int nPixels = lines_at_node(rank, nbNodes, o.height) * o.width;
                ret = MPI_Send(image, nPixels, MPI_BYTE, 0, 1, MPI_COMM_WORLD);
                check(ret == MPI_SUCCESS, "Node %d: failed to send to master", rank);
        }


        ret = MPI_Finalize();
        check(ret == MPI_SUCCESS, "Failed to finalize MPI");

        return 0;
}