TEST_F(WaveformGeneratorTest, shouldComputeMaxAndMinValuesFromStereoInput)
{
    WaveformBuffer buffer;

    const int samples_per_pixel = 300;

    SamplesPerPixelScaleFactor scale_factor(samples_per_pixel);
    WaveformGenerator generator(buffer, scale_factor);

    const int sample_rate = 44100;
    const int channels    = 2;
    const int BUFFER_SIZE = 1024;

    short samples[BUFFER_SIZE];
    memset(samples, 0, sizeof(samples));

    const int frames = BUFFER_SIZE / channels;

    bool result = generator.init(sample_rate, channels, BUFFER_SIZE);

    ASSERT_TRUE(result);
    ASSERT_TRUE(error.str().empty());

    // even indexes: left channel, odd indexes: right channel
    samples[0] = 100;
    samples[1] = 102;
    samples[200] = 98;
    samples[201] = 100;
    samples[400] = -98;
    samples[401] = -100;
    samples[598] = -100;
    samples[599] = -102;

    samples[600] = 197;
    samples[601] = 199;
    samples[800] = -200;
    samples[801] = -202;
    samples[900] = -197;
    samples[901] = -199;
    samples[1022] = 200;
    samples[1023] = 202;

    result = generator.process(samples, frames);
    ASSERT_TRUE(result);

    generator.done();

    // Check contents of buffer
    ASSERT_THAT(buffer.getSampleRate(), Eq(44100));
    ASSERT_THAT(buffer.getSamplesPerPixel(), Eq(300));
    ASSERT_THAT(buffer.getSize(), Eq(2)); // 512 / 300 = 1 remainder 212
                                          // => 2 output points total

    // Check min and max values are average of left and right channels
    ASSERT_THAT(buffer.getMinSample(0), Eq(-101));
    ASSERT_THAT(buffer.getMaxSample(0), Eq(101));

    ASSERT_THAT(buffer.getMinSample(1), Eq(-201));
    ASSERT_THAT(buffer.getMaxSample(1), Eq(201));
}
TEST_F(WaveformGeneratorTest, shouldComputeMaxAndMinValuesFromMonoInput)
{
    WaveformBuffer buffer;

    const int samples_per_pixel = 300;

    SamplesPerPixelScaleFactor scale_factor(samples_per_pixel);
    WaveformGenerator generator(buffer, scale_factor);

    const int sample_rate = 44100;
    const int channels    = 1;
    const int BUFFER_SIZE = 512;

    short samples[BUFFER_SIZE];
    memset(samples, 0, sizeof(samples));

    const int frames = BUFFER_SIZE / channels;

    bool result = generator.init(sample_rate, channels, BUFFER_SIZE);

    ASSERT_TRUE(result);
    ASSERT_TRUE(error.str().empty());

    // samples for first waveform data point
    samples[0] = 100;
    samples[100] = 98;
    samples[200] = -98;
    samples[299] = -102;

    // samples for second waveform data point
    samples[300] = 197;
    samples[400] = -200;
    samples[450] = -197;
    samples[511] = 202;

    result = generator.process(samples, frames);
    ASSERT_TRUE(result);

    generator.done();

    // Check contents of buffer
    ASSERT_THAT(buffer.getSampleRate(), Eq(44100));
    ASSERT_THAT(buffer.getSamplesPerPixel(), Eq(300));
    ASSERT_THAT(buffer.getSize(), Eq(2)); // 512 / 300 = 1 remainder 212
                                          // => 2 output points total

    // Check min and max values
    ASSERT_THAT(buffer.getMinSample(0), Eq(-102));
    ASSERT_THAT(buffer.getMaxSample(0), Eq(100));

    ASSERT_THAT(buffer.getMinSample(1), Eq(-200));
    ASSERT_THAT(buffer.getMaxSample(1), Eq(202));
}
TEST_F(WaveformGeneratorTest, shouldSetBufferAttributes)
{
    WaveformBuffer buffer;

    const int samples_per_pixel = 300;

    WaveformGenerator generator(buffer, samples_per_pixel);

    const int sample_rate = 44100;
    const int channels    = 2;
    const int BUFFER_SIZE = 1024;

    bool result = generator.init(sample_rate, channels, BUFFER_SIZE);

    ASSERT_TRUE(result);

    ASSERT_THAT(buffer.getSampleRate(), Eq(44100));
    ASSERT_THAT(buffer.getSamplesPerPixel(), Eq(300));
}
TEST_F(WaveformGeneratorTest, shouldSucceedIfEndTimeGreaterThanStartTime)
{
    WaveformBuffer buffer;
    DurationScaleFactor scale_factor(2.0, 3.0, 100);

    WaveformGenerator generator(buffer, scale_factor);

    const int sample_rate = 44100;
    const int channels    = 2;
    const int BUFFER_SIZE = 1024;

    bool result = generator.init(sample_rate, channels, BUFFER_SIZE);

    ASSERT_TRUE(result);
    ASSERT_TRUE(error.str().empty());

    ASSERT_THAT(generator.getSamplesPerPixel(), Eq(441));
    ASSERT_THAT(buffer.getSampleRate(), Eq(44100));
    ASSERT_THAT(buffer.getSamplesPerPixel(), Eq(441));
}
bool GdImageRenderer::create(
    const WaveformBuffer& buffer,
    const double start_time,
    const int image_width,
    const int image_height,
    const WaveformColors& colors,
    const bool render_axis_labels)
{
    if (start_time < 0.0) {
        error_stream << "Invalid start time: minimum 0\n";
        return false;
    }
    else if (start_time > MAX_START_TIME) {
        error_stream << "Invalid start time: maximum " << MAX_START_TIME << '\n';
        return false;
    }

    if (image_width < 1) {
        error_stream << "Invalid image width: minimum 1\n";
        return false;
    }

    if (image_height < 1) {
        error_stream << "Invalid image height: minimum 1\n";
        return false;
    }

    const int sample_rate = buffer.getSampleRate();

    if (sample_rate > MAX_SAMPLE_RATE) {
        error_stream << "Invalid sample rate: " << sample_rate
                     << " Hz, maximum " << MAX_SAMPLE_RATE << " Hz\n";
        return false;
    }

    const int samples_per_pixel = buffer.getSamplesPerPixel();

    if (samples_per_pixel > MAX_ZOOM) {
        error_stream << "Invalid zoom: maximum " << MAX_ZOOM << '\n';
        return false;
    }

    image_ = gdImageCreateTrueColor(image_width, image_height);

    if (image_ == nullptr) {
        error_stream << "Failed to create image\n";
        return false;
    }

    assert(sample_rate != 0);
    assert(samples_per_pixel != 0);

    image_width_        = image_width;
    image_height_       = image_height;
    start_time_         = start_time;
    sample_rate_        = buffer.getSampleRate();
    samples_per_pixel_  = samples_per_pixel;
    start_index_        = secondsToPixels(start_time);
    render_axis_labels_ = render_axis_labels;

    output_stream << "Image dimensions: " << image_width_ << "x" << image_height_ << " pixels"
                  << "\nSample rate: " << sample_rate_ << " Hz"
                  << "\nSamples per pixel: " << samples_per_pixel_
                  << "\nStart time: " << start_time_ << " seconds"
                  << "\nStart index: " << start_index_
                  << "\nBuffer size: " << buffer.getSize()
                  << "\nAxis labels: " << (render_axis_labels_ ? "yes" : "no") << std::endl;

    if (colors.hasAlpha()) {
        gdImageSaveAlpha(image_, 1);
        gdImageAlphaBlending(image_, 0);
    }

    initColors(colors);
    drawBackground();

    if (render_axis_labels_) {
        drawBorder();
    }

    drawWaveform(buffer);

    if (render_axis_labels_) {
        drawTimeAxisLabels();
    }

    return true;
}