#!/usr/bin/ruby

# Author: Daniel "Trizen" Șuteu
# License: GPLv3
# Date: 15th October 2013
# Translated to Sidef in 17 June 2015
# http://trizenx.blogspot.com
# Email: <echo dHJpemVueEBnbWFpbC5jb20K | base64 -d>

# Smart word wrap algorithm
# See: http://en.wikipedia.org/wiki/Word_wrap#Minimum_raggedness

# Review: http://trizenx.blogspot.ro/2013/11/smart-word-wrap.html

class SmartWordWrap(WIDTH=80) {
    method prepare_words(array) {

        var root = [];
        var len  = 0;

        for (var i = 0 ; i <= array.end ; i++) {
            var word_len = array[i].len;
            len += word_len;

            len > WIDTH && (
                word_len > WIDTH && (
                    len -= word_len;
                    array.splice(i, 1, array[i].split(WIDTH)...);
                    --i; next;
                );
                break;
            );

            root.append(Hash.new(array.ft(0, i).join(" ")
                => self.prepare_words(array.ft(i + 1, array.end))));
            ++len >= WIDTH && break;
        }

        root ? root : null;
    }

    method combine(root, hash) {

        var row = [];
        hash.each { |key, value|
            root.append(key);

            if (value.is_an(Array)) {
                value.each { |item|
                    row.append(self.combine(root, item)...);
                }
            }
            else {
                row = [[root...]];
            }
            root.pop;
        }

        row;
    }

    method find_best(arrays) {

        var best = Hash.new(
            score => Inf,
            value => [],
        );

        arrays.each { |array_ref|
            var score = 0;

            array_ref.each { |string|
                score += ::pow(WIDTH - string.len, 2);
            }

            score < best{:score} && (
                best{:score} = score;
                best{:value} = array_ref;
            );
        }

        best{:value};
    }

    method wrap(text, width) {

        # Temporarily modify the width
        local WIDTH = width if defined(width);

        # Split the text into words
        text.is_a(String) && text.words!;

        var lines = [];
        self.prepare_words(text).each { |path|
            lines.append(self.combine([], path)...);
        }

        self.find_best(lines).join("\n");
    }
}

var sww = SmartWordWrap();

var words = %w(aaa bb cc ddddd);
#var words = %w(Lorem ipsum dolor sit amet, consectetur adipiscing elit.);
var wrapped = sww.wrap(words, 6);

say wrapped;

wrapped == ['aaa', 'bb cc', 'ddddd'].join("\n") || die "error!";