/*
 * Copyright (c) 2014 TradertoolsFX
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

// Label modes
#define NUMERIC  0
#define LETTER   1
#define ROMAN    2

class ElliottWaveLabel {
    private:
        int m_mode;
        int m_num_waves;
        int m_fn_size;
        string m_fn_family;
        color m_color;
        string m_prefix;

        string wave_label(int idx);
        string wave_label_name(int cycle, int idx);
        string index_to_alpha(int idx);
        string int_to_roman(int n);
    public:
        ElliottWaveLabel(int mode,
                         int num_waves,
                         int fn_size,
                         string fn_family,
                         color color_,
                         string prefix);
        void draw(void);
};

ElliottWaveLabel::ElliottWaveLabel(int mode,
                                   int num_waves,
                                   int fn_size,
                                   string fn_family,
                                   color color_,
                                   string prefix)
{
    m_mode = mode;
    m_num_waves = num_waves;
    m_fn_size = fn_size;
    m_fn_family = fn_family;
    m_color = color_;
    m_prefix = prefix;
}

/* Draw a label on the chart for the current wave */
void ElliottWaveLabel::draw(void)
{
    int cycle=0, idx=0;
    datetime time;
    double price;
    string name;

    /* Loop through wave label objects to find the current name and wave index.
     * Once name is not found, the end has been reached and the name is the
     * new label name. */
    while (ObjectFind(name = wave_label_name(cycle, idx)) >= 0) {
        if (idx == (m_num_waves - 1)) { // Is this the end of the cycle?
            cycle++;
            idx = 0; // Reset wave index
        } else {
            idx++;
        }
    }

    // Set the drop coordinates
    time = WindowTimeOnDropped();
    price = WindowPriceOnDropped();

    ObjectCreate(name, OBJ_TEXT, 0, time, price);
    ObjectSetText(name, wave_label(idx), m_fn_size, m_fn_family, m_color);
}

/* Return the wave label string given the wave index idx */
string ElliottWaveLabel::wave_label(int idx)
{
    switch (m_mode) {
    case NUMERIC:
        return IntegerToString(idx + 1);
    case LETTER:
        return index_to_alpha(idx);
    case ROMAN:
        return int_to_roman(idx + 1);
    default:
        return "Invalid mode for wave label";
    }
}

/* Return the wave label's internal object name
 * given the cycle and wave index idx */
string ElliottWaveLabel::wave_label_name(int cycle, int idx)
{
    return StringConcatenate(m_prefix, cycle, "_", idx);
}

/* Return the capital letter(s) corresponding to idx,
 * where 0 corresponds to A, 1 to B, and so on.
 * If idx exceeds 25 (Z), the return value will be appended
 * such that 26 is AA, 27 is BB, and so on. */
string ElliottWaveLabel::index_to_alpha(int idx)
{
    string res = "";
    int repeat = 1;
    int c = 'A' + idx;

    // Just in case NUM_WAVES > 26
    while (c > 'Z') {
        c -= 26;
        repeat++;
    }

    while (repeat-- > 0) {
        res += CharToStr((char)c);
    }

    return res;
}

/* Return the roman numeral string representation given a positive integer n */
string ElliottWaveLabel::int_to_roman(int n)
{
    int digits[] = { 1, 4, 5, 9, 10, 40, 50, 90, 100, 400, 500, 900, 1000 };
    string romn[] = { "I", "IV", "V", "IX", "X", "XL",
                      "L", "XC", "C", "CD", "D", "CM", "M" };

    string roman = "";
    int sz = MathMin(ArraySize(romn), ArraySize(digits));
    int i = sz - 1;

    while (i >= 0) {
        if (n < digits[i]) {
            i--;
        } else {
            n -= digits[i];
            roman = StringConcatenate(roman, romn[i]);
        }
    }

    return roman;
}
