
// DashingState.cpp - track dashing state for stroking

#include <assert.h>

#include <cmath>

#include "DashingState.hpp"

using std::vector;

DashingState::DashingState(vector<float> dash_array, float dash_offset)
    // these get subsequently overriden if dashing
    : dashing(false)  // initial assumption
    , dash_on(true)
    , current_segment(0)
    , distance_into_pattern(0)
    , accumulated_length(0)
{
    const size_t dash_count = dash_array.size();
    if (dash_count > 0) {
        // Compute total length of dash array pattern...
        double dash_pattern_length = 0;
        for (size_t i=0; i<dash_count; i++) {
            assert(dash_array[i] >= 0);
            dash_pattern_length += dash_array[i];
        }
        // A dash pattern of zero total length acts like no dashing.
        if (dash_pattern_length > 0) {
            dashing = true;
            // Is the pattern array size odd?
            double adjusted_pattern_length = dash_pattern_length;
            if (dash_count & 1) {
                // Yes, pattern repeats itself so adjust length to double.
                // So dash patern 10,20,30 acts like 10,20,30,10,20,30
                adjusted_pattern_length *= 2;
            }

            // Given dash offset, how far into the path (modulo dash 
            // pattern length) does the dash pattern begin in the stroke?
            double current_dash_offset = dash_offset;
            distance_into_pattern = fmod(current_dash_offset, adjusted_pattern_length);
            if (distance_into_pattern < 0) {  // handle negative dash offset situations
                distance_into_pattern += adjusted_pattern_length;
            }
            assert(0 <= distance_into_pattern && distance_into_pattern < adjusted_pattern_length);

            // Determine dash offset's implied segment, dash_on state, and accumulated length.
            for (size_t i=0; i<dash_count; i++) {
                double length_to_segment_end = accumulated_length + dash_array[i];
                if (distance_into_pattern <= length_to_segment_end) {
                    current_segment = i;
                    break;
                }
                dash_on = !dash_on;
                accumulated_length = length_to_segment_end;
            }
            // Is the dash pattern length is odd AND the beginning in second have of the stroke?
            if (dash_count & 1 && distance_into_pattern >= dash_pattern_length) {
                for (size_t i=0; i<dash_count; i++) {
                    double length_to_segment_end = accumulated_length + dash_array[i];
                    if (distance_into_pattern <= length_to_segment_end) {
                        current_segment = i;
                        break;
                    }
                    dash_on = !dash_on;
                    accumulated_length = length_to_segment_end;
                }
            }
        }
    }
}

// Copy constructor
DashingState::DashingState(const DashingState &src)
    : dashing(src.dashing)
    , dash_on(src.dash_on)
    , current_segment(src.current_segment)
    , distance_into_pattern(src.distance_into_pattern)
    , accumulated_length(src.accumulated_length)
{}

void DashingState::advancePatternElement(const vector<float> &dash_array)
{
    // Advance to the next segment
    size_t ndx = current_segment;
    if (dash_array.size() & 1) {
        ndx = ndx % dash_array.size();
    }
    accumulated_length += dash_array[ndx];
    distance_into_pattern = accumulated_length;
    current_segment++;
    dash_on = !dash_on;  // toggle
    if (current_segment >= dash_array.size()) {
        assert(dash_on || dash_array.size() & 1);
        current_segment = 0;
        distance_into_pattern = 0;
        accumulated_length = 0;
    }
}

SeamDashingState::SeamDashingState(vector<float> dash_array, float dash_offset)
    : DashingState(dash_array, dash_offset)
    , use_hull_seam(true)
{
    if (!DashOn()) {
        ignoreHullSeam();
    }
}

// Copy constructor
SeamDashingState::SeamDashingState(const SeamDashingState &src)
    : DashingState(src)
    , use_hull_seam(src.use_hull_seam)
{}


