// ****************************************************************************
//  Method: Readback
//
//  Purpose: Reads back the image buffer from IceT.
//
//  Programmer: Tom Fogal
//  Creation:   June 20, 2008
//
//  Modifications:
//
//    Tom Fogal, Tue Jul  1 11:06:55 EDT 2008
//    Hack the number of scalars in the vtkImageData we create to be 4, to
//    match the kind of buffer IceT gives us.  This allows us to skip an
//    expensive GL_RGBA -> GL_RGB conversion.
//    Also, use a void*; don't know why it was a uchar* before ...
//
//    Tom Fogal, Wed Jul  2 11:05:07 EDT 2008
//    Readback and send/recv the Z buffer (unconditionally...).
//
//    Tom Fogal, Thu Jul 17 17:02:40 EDT 2008
//    Repurposed viewported argument for a boolean to grab Z.
//
//    Tom Fogal, Mon Jul 28 14:44:28 EDT 2008
//    Don't ask IceT for Z if we're not going to use it anyway.
//
//    Tom Fogal, Mon Sep  1 14:21:46 EDT 2008
//    Removed asserts / dependence on NDEBUG.
//
//    Hank Childs, Mon Dec 29 18:24:05 CST 2008
//    Make an image have 3 components, not 4, since 3 is better supported
//    throughout VisIt (including saving TIFFs).
//
//    Hank Childs, Thu Jan 15 11:07:53 CST 2009
//    Changed GetSize call to GetCaptureRegion, since that works for 2D.
//
//    Hank Childs, Fri Feb  6 15:47:17 CST 2009
//    Fix memory leak.
//
//    Burlen Loring, Tue Sep  1 14:26:30 PDT 2015
//    sync up with network manager(base class) order compositing refactor
//
// ****************************************************************************
avtImage_p
IceTNetworkManager::Readback(VisWindow * const viswin,
                             bool readZ) const
{
    GLboolean have_image;

    ICET(icetGetBooleanv(ICET_COLOR_BUFFER_VALID, &have_image));

    int width=-42, height=-42, width_start, height_start;
    // This basically gets the width and the height.
    // The distinction is for 2D rendering, where we only want the
    // width and the height of the viewport.
    viswin->GetCaptureRegion(width_start, height_start, width, height,
                             renderState.viewportedMode);

    GLubyte *pixels = NULL;
    GLuint *depth = NULL;

    if(readZ && have_image == GL_TRUE)
    {
        depth = icetGetDepthBuffer();
        DEBUG_ONLY(ICET_CHECK_ERROR);
    }
    // We can't delete pointers IceT gives us.  However if we're a receiving
    // node, we'll dynamically allocate our buffers and thus need to deallocate
    // them.
    bool dynamic = false;

    if(have_image == GL_TRUE)
    {
        // We have an image.  First read it back from IceT.
        pixels = icetGetColorBuffer();
        DEBUG_ONLY(ICET_CHECK_ERROR);

        this->VerifyColorFormat(); // Bail out if we don't get GL_RGBA data.
    } else {
        // We don't have an image -- we need to receive it from our buddy.
        // Purpose of static pixel_ptr ... if I delete this memory too soon (i.e. along
        // with "depth"), then there is a crash ... it is being used after the function
        // exits.  So just wait until the next render to free it.
        static GLubyte *pixel_ptr = NULL;
        if (pixel_ptr != NULL)
           delete [] pixel_ptr;
        pixel_ptr = new GLubyte[4*width*height];
        pixels = pixel_ptr;
        depth = new GLuint[width*height];

        dynamic = true;
    }
    SendImageToRenderNodes(width, height, readZ, pixels, depth);

    vtkImageData *image = avtImageRepresentation::NewImage(width, height);
    // NewImage assumes we want a 3-component ("GL_RGB") image, but IceT gives
    // us back data in a GL_RGBA format.  So we just reset the number of
    // components and reallocate the data; unfortunately this means we do an
    // allocate in NewImage and then immediately throw it away when doing an
    // allocate here.
    image->AllocateScalars(VTK_UNSIGNED_CHAR, 3);
    {
        unsigned char *img_pix = (unsigned char *) image->GetScalarPointer();
        const int numPix = width*height;
        for (int i = 0 ; i < numPix ; i++)
        {
            *img_pix++ = *pixels++;
            *img_pix++ = *pixels++;
            *img_pix++ = *pixels++;
            pixels++; // Alpha
        }
    }
    float *visit_depth_buffer = NULL;

    if(readZ)
    {
        debug4 << "Converting depth values ..." << std::endl;
        visit_depth_buffer = utofv(depth, width*height);
    }

    avtSourceFromImage screenCapSrc(image, visit_depth_buffer);
    avtImage_p visit_img = screenCapSrc.GetTypedOutput();
    visit_img->Update(screenCapSrc.GetGeneralContract());
    visit_img->SetSource(NULL);
    image->Delete();
    delete[] visit_depth_buffer;
    if(dynamic)
    {
        delete[] depth;
    }

    debug3 << "Readback complete." << std::endl;

    return visit_img;
}
int DisplayNoDraw(int argc, char *argv[])
{
    int result = TEST_PASSED;
    int i;
    GLint rank, num_proc;

    /* To remove warning */
    (void)argc;
    (void)argv;

    icetGetIntegerv(ICET_RANK, &rank);
    icetGetIntegerv(ICET_NUM_PROCESSES, &num_proc);

    printf("Starting DisplayNoDraw.\n");

    global_rank = rank;

    printf("Setting tile.");
    icetResetTiles();
    icetAddTile(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0);

    icetDrawFunc(draw);

    if (rank == 0) {
        icetBoundingBoxf(100.0, 101.0, 100.0, 101.0, 100.0, 101.0);
    } else {
        icetBoundingBoxf(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0);
    }

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-0.5, 0.5, -0.5, 0.5, -0.5, 0.5);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    glDisable(GL_LIGHTING);
    glColor4f(1.0, 1.0, 1.0, 1.0);

    for (i = 0; i < STRATEGY_LIST_SIZE; i++) {
        GLubyte *color_buffer;

        icetStrategy(strategy_list[i]);
        printf("\n\nUsing %s strategy.\n", icetGetStrategyName());

        for (iteration = 0; iteration < num_proc; iteration++) {
            printf("Blank tile is rank %d\n", iteration);

            icetDrawFrame();
            swap_buffers();

            if (   (rank == 0)
                && (num_proc > 1)
               /* This last case covers when there is only 2 processes,
                * the root, as always, is not drawing anything and the
                * other process is drawing the clear screen. */
                && ((num_proc > 2) || (iteration != 1)) ) {
                int p;
                int bad_count = 0;
                printf("Checking pixels.\n");
                color_buffer = icetGetColorBuffer();
                for (p = 0; p < SCREEN_WIDTH*SCREEN_HEIGHT*4; p++) {
                    if (color_buffer[p] != 255) {
                        char filename[256];
                        printf("BAD PIXEL %d.%d\n", p/4, p%4);
                        printf("    Expected 255, got %d\n", color_buffer[p]);
                        bad_count++;
                        if (bad_count >= 10) {
                            result = TEST_FAILED;
                            sprintf(filename, "DisplayNoDraw_%s_%d.ppm",
                                    icetGetStrategyName(), iteration);
                            write_ppm(filename, color_buffer,
                                      SCREEN_WIDTH, SCREEN_HEIGHT);
                            break;
                        }
                    }
                }
            }
        }
    }

    finalize_test(result);
    return result;
}
int BlankTiles(int argc, char *argv[])
{
    int i, j, x, y;
    GLubyte *cb;
    int result = TEST_PASSED;
    GLint rank, num_proc;

    /* To remove warning */
    (void)argc;
    (void)argv;

    icetGetIntegerv(ICET_RANK, &rank);
    icetGetIntegerv(ICET_NUM_PROCESSES, &num_proc);

    glClearColor(0.0, 0.0, 0.0, 0.0);

    icetDrawFunc(draw);
    icetBoundingBoxf(-0.5, 0.5, -0.5, 0.5, -0.5, 0.5);

    for (i = 0; i < STRATEGY_LIST_SIZE; i++) {
        int tile_dim;

        icetStrategy(strategy_list[i]);
        printf("\n\nUsing %s strategy.\n", icetGetStrategyName());

        for (tile_dim = 1; tile_dim*tile_dim <= num_proc; tile_dim++) {
            printf("\nRunning on a %d x %d display.\n", tile_dim, tile_dim);
            icetResetTiles();
            for (y = 0; y < tile_dim; y++) {
                for (x = 0; x < tile_dim; x++) {
                    icetAddTile(x*SCREEN_WIDTH, y*SCREEN_HEIGHT,
                                SCREEN_WIDTH, SCREEN_HEIGHT, y*tile_dim + x);
                }
            }

            printf("Rendering frame.\n");
            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            glOrtho(-1, tile_dim*2-1, -1, tile_dim*2-1, -1, 1);
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
            icetDrawFrame();
            swap_buffers();

            if (rank == 0) {
                printf("Rank == 0, tile should have stuff in it.\n");
            } else if (rank < tile_dim*tile_dim) {
                printf("Checking returned image.\n");
                cb = icetGetColorBuffer();
                for (j = 0; j < SCREEN_WIDTH*SCREEN_HEIGHT*4; j++) {
                    if (cb[j] != 0) {
                        printf("Found bad pixel!!!!!!!!\n");
                        result = TEST_FAILED;
                        break;
                    }
                }
            } else {
                printf("Not a display node.  Not testing image.\n");
            }
        }
    }

    printf("Cleaning up.\n");

    finalize_test(result);
    return result;
}
int BoundsBehindViewer(int argc, char * argv[])
{
    float mat[16];

    /* To remove warning */
    (void)argc;
    (void)argv;

    GLint rank;
    icetGetIntegerv(ICET_RANK, &rank);

    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

    icetDrawFunc(draw);
    icetStrategy(ICET_STRATEGY_REDUCE);

    icetBoundingBoxf(-1.0, 1.0, -1.0, 1.0, -0.0, 0.0);

  /* We're just going to use one tile. */
    icetResetTiles();
    icetAddTile(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0);

  /* Set up the transformation such that the quad in draw should cover the
     entire screen, but part of it extends behind the viewpoint.  Furthermore, a
     naive division by w will show all points to the right of the screen (which,
     of course, is wrong). */
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.0, 0.0, -1.5);
    glRotatef(10.0, 0.0, 1.0, 0.0);
    glScalef(10.0, 10.0, 10.0);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustum(-1.0, 1.0, -1.0, 1.0, 1.0, 2.0);

    printf("Modelview matrix:\n");
    glGetFloatv(GL_MODELVIEW_MATRIX, mat);
    PrintMatrix(mat);
    printf("Projection matrix:\n");
    glGetFloatv(GL_PROJECTION_MATRIX, mat);
    PrintMatrix(mat);

  /* Other normal OpenGL setup. */
    glEnable(GL_DEPTH_TEST);
    glDisable(GL_LIGHTING);
    glColor3f(1.0, 1.0, 1.0);

  /* All the processes have the same data.  Go ahead and tell IceT. */
    icetDataReplicationGroupColor(0);

    icetDrawFrame();

  /* Test the resulting image to make sure the polygon was drawn over it. */
    if (rank == 0) {
        GLuint *cb = (GLuint *)icetGetColorBuffer();
        if (cb[0] != 0xFFFFFFFF) {
            printf("First pixel in color buffer wrong: 0x%x\n", cb[0]);
            finalize_test(TEST_FAILED);
            return TEST_FAILED;
        }
    }

    finalize_test(TEST_PASSED);
    return TEST_PASSED;
}