//
// CSV Parsing and Creating Artboards adobe illustrator script

var doc = app.activeDocument;
doc.rulerOrigin = [0,doc.height];
var alertCode = "Well done!";

// DIALOG
// ======
var dialog = new Window("dialog"); 
    dialog.text = "Параметры"; 
    dialog.orientation = "column"; 
    dialog.alignChildren = ["right","top"]; 
    dialog.spacing = 14; 
    dialog.margins = 20; 

var group = dialog.add("group", undefined, {name: "group"}); 
    group.orientation = "row"; 
    group.alignChildren = ["right","center"]; 
    group.spacing = 14; 
    group.margins = 0; 

var parameterLabel = group.add("statictext", undefined, undefined, {name: "parameterLabel"}); 
    parameterLabel.text = "Код объекта (будет вписан в кодирование):"; 

var prefix = group.add('edittext {properties: {name: "prefix"}}'); 
    prefix.text = 'XXX'; 
    prefix.preferredSize.width = 70;

var group = dialog.add("group", undefined, {name: "group"}); 
    group.orientation = "row"; 
    group.alignChildren = ["right","center"]; 
    group.spacing = 14; 
    group.margins = 0; 

var cdeLabel = group.add("statictext", undefined, undefined, {name: "cdeLabel"}); 
    cdeLabel.text = "Номера столбцов кодирования:"; 

var inputCodeColumn = group.add('edittext {properties: {name: "inputCodeColumn"}}'); 
    inputCodeColumn.text = [1,2,3]; 
    inputCodeColumn.preferredSize.width = 70;
    inputCodeColumn.value = [0,1,2];

var group = dialog.add("group", undefined, {name: "group"}); 
    group.orientation = "row"; 
    group.alignChildren = ["right","center"]; 
    group.spacing = 14; 
    group.margins = 0; 

var msgLabel = group.add("statictext", undefined, undefined, {name: "msgLabel"}); 
    msgLabel.text = "Номер столбца с содержанием:"; 

var inputMsgColumn = group.add('edittext {properties: {name: "inputMsgColumn"}}'); 
    inputMsgColumn.text = 5; 
    inputMsgColumn.preferredSize.width = 70;
    inputMsgColumn.value = 4;

var group = dialog.add("group", undefined, {name: "group"}); 
    group.orientation = "row"; 
    group.alignChildren = ["right","center"]; 
    group.spacing = 14; 
    group.margins = 0; 

var dimLabel = group.add("statictext", undefined, undefined, {name: "dimLabel"}); 
    dimLabel.text = "Номер столбца с размерами:"; 

var inputDimColumn = group.add('edittext {properties: {name: "inputDimColumn"}}'); 
    inputDimColumn.text = 7; 
    inputDimColumn.preferredSize.width = 70;
    inputDimColumn.value = 6;

var group = dialog.add("group", undefined, {name: "group"}); 
    group.orientation = "row"; 
    group.alignChildren = ["right","center"]; 
    group.spacing = 14; 
    group.margins = 0; 

var scaleLabel = group.add("statictext", undefined, undefined, {name: "scaleLabel"}); 
    scaleLabel.text = "Масштаб 1:10";
var scaleCheckbox = group.add("checkbox", undefined, undefined, {name: "scaleCheckbox"}); 
    scaleCheckbox.preferredSize.width = 26;
    scaleCheckbox.value = true;
var sortLabel = group.add("statictext", undefined, undefined, {name: "sortLabel"}); 
    sortLabel.text = "Сортировка по высоте:"; 
var sortCheckbox = group.add("checkbox", undefined, undefined, {name: "sortCheckbox"}); 
    sortCheckbox.preferredSize.width = 26;
    sortCheckbox.value = false;

inputCodeColumn.onChanging = function() {
  inputCodeColumn.value = inputCodeColumn.text.split(",");
  for (var i = inputCodeColumn.value.length - 1; i >= 0; i--) {
      inputCodeColumn.value[i] = parseInt(inputCodeColumn.value[i]);
      inputCodeColumn.value[i]--;
  }
};


inputMsgColumn.onChanging = function() {
    inputMsgColumn.value = parseInt(inputMsgColumn.text) - 1;
}

inputDimColumn.onChanging = function() {
    inputDimColumn.value = parseInt(inputDimColumn.text) - 1;
}

var OK = dialog.add("button", undefined, undefined, {name: "OK"}); 
    OK.text = "OK"; 

dialog.show();

var messageColumn = inputMsgColumn.value;
var dimensionsColumn = inputDimColumn.value;
var scale = 1.0;
if (scaleCheckbox.value) scale = 10.0;

// Graphics parameters

var artboardsGap = 50;     // gap between artboards

var workplaneX = doc.width * scale / 2.83464567;      // Максимальные Canvas
var workplaneY = doc.height * scale / 2.83464567;

// Backgound parameters
var backLayer = doc.layers.add();
backLayer.name = "BackLayer";
var backColor = new CMYKColor();
backColor.cyan = 0;  
backColor.magenta = 30;  
backColor.yellow = 100;
backColor.black = 0;

// Signage Code parameters

var codeLayer = doc.layers.add();
codeLayer.name = "Codes";

var codeSize = 16 / scale;         //Font size for codes
var codeFont = "Helvetica"

var codeColor = new CMYKColor();  
codeColor.cyan = 0;  
codeColor.magenta = 0;  
codeColor.yellow = 0;
codeColor.black = 60;

// Signage Message parameters

var messageLayer = doc.layers.add();
messageLayer.name = "Messages";

var messageSize = 36 / scale;         //Font size for messages
var messageFont = "Helvetica"

var messageColor = new CMYKColor();  
messageColor.cyan = 40;  
messageColor.magenta = 0;  
messageColor.yellow = 0;
messageColor.black = 90;

alert(main()); // exit with code



function main() {

// read csv file and make 2D-array named "rows"
var csvFile = File.openDialog('Select a CSV File', function (f) { return (f instanceof Folder) || f.name.match(/\.csv$/i);});
csvFile.open("r");
var contents = csvFile.read();
csvFile.close();
var rows = parseCsv(contents);      //make 2D-array named "rows"

rows.splice(0, 1); // удалить первую строку с заголовками

var errorRows = '';

for (var i=0; i<rows.length; i++) {
    var dims = parseDimensions(rows[i][dimensionsColumn]);
    if (isNaN(dims.x) || isNaN(dims.y)) errorRows += (', ' + (i+2).toString());
    rows[i].push(dims.x);
    rows[i].push(dims.y);
}

if (errorRows != '') alertCode = "Ошибки в размерах" + errorRows + " строка";


if (alertCode != "Well done!") {
    messageLayer.remove();
    codeLayer.remove();
    backLayer.remove();
    return alertCode;}  // exit main() with error
if (sortCheckbox.value) rows.sort(function(a,b) {return a[rows[0].length-1]-b[rows[0].length-1]});    // сортировка по numberOfColumns+1 (это dims.y)
    
// теперь расставляем артборды
var currentX = 0;
var currentY = 0;
var prevH = 0;

for (var i=0; i<rows.length; i++) {                 
    var currentW = rows[i][rows[i].length-2];        //get width value of current row
    var currentH = rows[i][rows[i].length-1];      //get height value of current row

    if (currentX+currentW > workplaneX) {
        currentX = 0;
        currentY += (prevH + artboardsGap);
        prevH = currentH;
    }

    if (currentY+currentH > workplaneY) alertCode = "Artboard doesn't fit Canvas";
    var abrd = doc.artboards.add(mm2points(currentX,currentY,currentW,currentH));

    var backRect = backLayer.pathItems.rectangle(-mm(currentY),mm(currentX),mm(currentW),mm(currentH));
    backRect.fillColor = backColor;
    backRect.strokeColor = new NoColor();

    var codeText = prefix.text;
    for (var k = 0; k < inputCodeColumn.value.length; k++) {
        codeText += ('_' + rows[i][inputCodeColumn.value[k]]);
    }
    codeText += ('_' + currentW.toString() + 'x' + currentH.toString());

    abrd.name = codeText;

    var label = codeLayer.textFrames.add();
    with(label){
    textRange.characterAttributes.size = codeSize;
    textRange.characterAttributes.fillColor = codeColor;
    textRange.characterAttributes.textFont = app.textFonts.getByName(codeFont);
    contents = codeText;    //////// code columns
    }
    label.translate(mm(currentX) + label.textRange.characterAttributes.size, -mm(currentY) - label.height);

    var message = messageLayer.textFrames.add();
    with(message){
    textRange.characterAttributes.size = messageSize;
    textRange.characterAttributes.fillColor = messageColor;
    textRange.characterAttributes.textFont = app.textFonts.getByName(messageFont);
    contents = rows[i][messageColumn];    //////// message column
    }
    message.translate(mm(currentX) + mm(artboardsGap)/2, -mm(currentY) - mm(artboardsGap)/2);

    var rectText = messageLayer.pathItems.rectangle(message.top, message.left, message.width, message.height);
    var messageFrame = messageLayer.textFrames.areaText(rectText);
    message.textRange.move(messageFrame, ElementPlacement.PLACEATEND);
    message.remove();


    currentX += (currentW + artboardsGap);
    if (prevH < currentH) prevH = currentH;
}

doc.artboards[0].remove();

backLayer.locked = true;
codeLayer.locked = true;

redraw();
return alertCode;
}


function mm2points(x,y,w,h) {
    var dims = [];
    dims[0] = mm(x);
    dims[1] = mm(-y);
    dims[2] = mm(x+w);
    dims[3] = mm(-y-h);
    return dims;
}

function mm(n) {
  return n * 2.83464567 / scale;       // в масштабе!!!!
}

function parseDimensions(text) {
    var myArray = text
    .replace("X", "*")
    .replace("Х", "*")
    .replace("x", "*")
    .replace("х", "*")
    .replace("_", "*")
    .replace(" ", "*")
    .split("*");
    var dimensions = {
        x: parseInt(myArray[0]),
        y: parseInt(myArray[1])
    };
    return dimensions;
}


function parseCsv(contents) {
    // contents: String = contents of a CSV file
    // Returns: Array [row][column]
    var c = ""; // Character at index.
    var d = ";"; // Delimiter.
    var index = 0;
    var maxIndex = contents.length - 1;
    var q = false; // "Are we in quotes?"
    var result = []; // Array of rows.
    var row = []; // Array of columns.
    var rowSum = 0; // Count of row contents.
    var v = ""; // Column value.
    while (index < contents.length) {
        c = contents[index];
        if (q) { // In quotes.
            if (c == "\"") {
                // Found quote; look ahead for another.
                if (index < maxIndex && contents[index + 1] == "\"") {
                    // Found another quote means escaped.
                    // Increment and add to column value.
                    index++;
                    v += c;
                } else {
                    // Next character not a quote; last quote not escaped.
                    q = !q; // Toggle "Are we in quotes?"
                }
            } else {
                // Add character to column value.
                v += c;
            }
        } else { // Not in quotes.
            if (c == "\"") {
                // Found quote.
                q = !q; // Toggle "Are we in quotes?"
            } else if (c == "\n" || c == "\r") {
                // Reached end of line.
                // Test for CRLF.
                if (c == "\r" && index < maxIndex) {
                    if (contents[index + 1] == "\n") {
                        // Skip trailing newline.
                        index++;
                    }
                }
                // Column and row complete.
                row.push(v);
                rowSum += v.length;
                if (rowSum) {
                    // Add row only when it has content.
                    result.push(row);
                }
                v = "";
                row = [];
                rowSum = 0;
            } else if (c == d) {
                // Found delimiter; column complete.
                row.push(v);
                rowSum += v.length;
                v = "";
            } else {
                // Add character to column value.
                v += c;
            }
        }
        if (index == maxIndex) {
            // Reached end of data; flush.
            row.push(v);
            rowSum += v.length;
            if (rowSum) {
                // Add row only when it has content.
                result.push(row);
            }
            break;
        }
        index++;
    }
    return result;
}
