using System;

using sharpallegro5;

using ALLEGRO_DISPLAY = System.IntPtr;
using ALLEGRO_BITMAP = System.IntPtr;
using clock_t = System.Int64;
using System.Diagnostics;

/*
 *    Benchmark for memory blenders.
 */
public class ex_blend_bench : AllegroExample
{
    /* Do a few un-timed runs to switch CPU to performance mode and cache
     * data and so on - seems to make the results more stable here.
     * Also used to guess the number of timed iterations.
     */
    const int WARMUP = 100;
    /* How many seconds the timing should approximately take - a fixed
     * number of iterations is not enough on very fast systems but takes
     * too long on slow systems.
     */
    const float TEST_TIME = 5.0f;

    enum Mode
    {
        ALL,
        PLAIN_BLIT,
        SCALED_BLIT,
        ROTATE_BLIT
    };

    static string[] names = {
        "", "Plain blit", "Scaled blit", "Rotated blit"
    };

    static ALLEGRO_DISPLAY display;

    static void step(Mode mode, ALLEGRO_BITMAP b2)
    {
        switch (mode)
        {
            case Mode.ALL: break;
            case Mode.PLAIN_BLIT:
                al_draw_bitmap(b2, 0, 0, 0);
                break;
            case Mode.SCALED_BLIT:
                al_draw_scaled_bitmap(b2, 0, 0, 320, 200, 0, 0, 640, 480, 0);
                break;
            case Mode.ROTATE_BLIT:
                al_draw_scaled_rotated_bitmap(b2, 10, 10, 10, 10, 2.0f, 2.0f,
                   (float)(ALLEGRO_PI / 30), 0);
                break;
        }
    }

    /* al_get_current_time() measures wallclock time - but for the benchmark
     * result we prefer CPU time so clock() is better.
     */
    static double current_clock()
    {
        clock_t c = DateTime.Now.Ticks; // clock();
        return (double)c / TimeSpan.TicksPerSecond; //CLOCKS_PER_SEC;
    }

    static bool do_test(Mode mode)
    {
        ALLEGRO_STATE state;
        ALLEGRO_BITMAP b1;
        ALLEGRO_BITMAP b2;
        int REPEAT;
        double t0, t1;
        int i;

        al_set_new_bitmap_flags(ALLEGRO_MEMORY_BITMAP);

        b1 = al_load_bitmap("data/mysha.pcx");
        if (b1 == IntPtr.Zero)
        {
            abort_example("Error loading data/mysha.pcx\n");
            return false;
        }

        b2 = al_load_bitmap("data/allegro.pcx");
        if (b2 == IntPtr.Zero)
        {
            abort_example("Error loading data/mysha.pcx\n");
            return false;
        }

        al_set_target_bitmap(b1);
        al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_INVERSE_ALPHA);
        step(mode, b2);

        /* Display the blended bitmap to the screen so we can see something. */
        al_store_state(ref state, ALLEGRO_STATE_ALL);
        al_set_target_backbuffer(display);
        al_set_blender(ALLEGRO_ADD, ALLEGRO_ONE, ALLEGRO_ZERO);
        al_draw_bitmap(b1, 0, 0, 0);
        al_flip_display();
        al_restore_state(ref state);

        log_printf("Benchmark: {0}\n", names[(int)mode]);
        log_printf("Please wait...\n");

        /* Do warmup run and estimate required runs for real test. */
        t0 = current_clock();
        for (i = 0; i < WARMUP; i++)
        {
            step(mode, b2);
        }
        t1 = current_clock();
        REPEAT = (int)(TEST_TIME * 100 / (t1 - t0));

        /* Do the real test. */
        t0 = current_clock();
        for (i = 0; i < REPEAT; i++)
        {
            step(mode, b2);
        }
        t1 = current_clock();

        // TODO: double numeric conversion/rounding to be fixed
        log_printf("Time = {0} s, {1} steps\n",
           t1 - t0, REPEAT);
        log_printf("{0}: {1} FPS\n", names[(int)mode], REPEAT / (t1 - t0));

        al_destroy_bitmap(b1);
        al_destroy_bitmap(b2);

        return true;
    }

    static int Main(string[] argv)
    {
        Mode mode = Mode.ALL;
        int i;

        if (argv.Length > 1)
        {
            i = int.Parse(argv[1]);
            switch (i)
            {
                case 0:
                    mode = Mode.PLAIN_BLIT;
                    break;
                case 1:
                    mode = Mode.SCALED_BLIT;
                    break;
                case 2:
                    mode = Mode.ROTATE_BLIT;
                    break;
            }
        }

        if (!al_init())
            return 1;

        open_log();

        al_init_image_addon();
        al_init_primitives_addon();

        display = al_create_display(640, 480);
        if (display == IntPtr.Zero)
        {
            abort_example("Error creating display\n");
            return 1;
        }

        if (mode == Mode.ALL)
        {
            for (mode = Mode.PLAIN_BLIT; mode <= Mode.ROTATE_BLIT; mode++)
            {
                do_test(mode);
            }
        }
        else
        {
            do_test(mode);
        }

        al_destroy_display(display);

        close_log(true);

        return 0;
    }
}