var Windows_Partition = function(s, type){
    this.error = false;
    this.origin = s;
    this.obj = [];
    this.xml = '';
    this.type = type || 'BIOS';
    this.version = ''

    this.init = function(){
        this.refreshObj(this.origin);
        this.refreshXml(this.obj);
        return this;
    }

    this.refreshXml = function(_obj){
        var obj = _obj || this.obj
        if(obj.length == 0){
            this.xml = '';
            return true;
        }
        let _xml = '';
        let WillShowUI = '<WillShowUI>OnError</WillShowUI>';
        for(let i = 0; i<obj.length; i++){
            let CreatePartitions = '';
            let ModifyPartitions = '';
            for(let j = 0; j<obj[i].CreatePartitions.length; j++){
                CreatePartitions += '        <CreatePartition wcm:action="add">\n';
                if(typeof obj[i].CreatePartitions[j].Order == 'undefined'){
                    this.error = true;
                    return false;
                }else{
                    CreatePartitions += '            <Order>' + (j+1) + '</Order>\n';
                }
                if(this.type == 'UEFI' && j == 0 && obj[i].CreatePartitions[j].Type != 'EFI'){
                    this.error = true;
                    return false;
                }
                if(typeof obj[i].CreatePartitions[j].Size !== 'undefined'){
                    if(/[^\d]/.test(obj[i].CreatePartitions[j].Size)){
                        this.error = true;
                        return false;
                    }else{
                        CreatePartitions += '            <Size>' + obj[i].CreatePartitions[j].Size + '</Size>\n';
                    }
                }else{
                    if( j !== obj[i].CreatePartitions.length - 1){  //不是最后一个order
                        this.error = true;
                        return false;
                    }else{
                        CreatePartitions += '            <Extend>true</Extend>\n';
                    }
                }
                if(typeof obj[i].CreatePartitions[j].Type == 'undefined'){
                    this.error = true;
                    return false;
                }else{
                    CreatePartitions += "            <Type>"+ obj[i].CreatePartitions[j].Type +"</Type>\n";
                }
                CreatePartitions += "        </CreatePartition>\n";
            }
            //
            for(let j = 0; j<obj[i].ModifyPartitions.length; j++){
                ModifyPartitions += '        <ModifyPartition wcm:action="add">\n';
                if(this.type == 'BIOS'){
                    if(typeof obj[i].ModifyPartitions[j].Order !== 'undefined' && obj[i].ModifyPartitions[j].Order == 1){
                        ModifyPartitions += '            <Active>true</Active>\n';
                    }else{
                        if(j == 0){
                            ModifyPartitions += '            <Active>true</Active>\n';
                        }
                    }
                }else{ //UEFI label
                    if(obj[i].ModifyPartitions[j].Order == 1 && obj[i].ModifyPartitions[j].Label != 'EFI'){
                        this.error = true;
                        return false;
                    }
                }
                //FAT32,NTFS
                if(typeof obj[i].ModifyPartitions[j].Format !== 'undefined' || obj[i].ModifyPartitions[j].Format == 'FAT32' || obj[i].ModifyPartitions[j].Format == 'NTFS'){
                    ModifyPartitions += '            <Format>' + obj[i].ModifyPartitions[j].Format + '</Format>\n';
                }else{
                    this.error = true;
                    return false;
                }
                //Label
                if(typeof obj[i].ModifyPartitions[j].Label !== 'undefined'){
                    if(/[^0-9a-zA-Z]/.test(obj[i].ModifyPartitions[j].Label)){  //卷标只能字母数字
                        this.error = true;
                        return false;
                    }
                    ModifyPartitions += '            <Label>' + obj[i].ModifyPartitions[j].Label + '</Label>\n';
                }else{
                    ModifyPartitions += '            <Label></Label>\n';
                }
                //Order
                if(typeof obj[i].ModifyPartitions[j].Order !== 'undefined'){
                    ModifyPartitions += '            <Order>' + obj[i].ModifyPartitions[j].Order + '</Order>\n';
                    ModifyPartitions += '            <PartitionID>' + obj[i].ModifyPartitions[j].Order + '</PartitionID>\n';
                }else{
                    ModifyPartitions += '            <Order>' + (j+1) + '</Order>\n';
                    ModifyPartitions += '            <PartitionID>' + (j+1) + '</PartitionID>\n';
                }
                ModifyPartitions += "        </ModifyPartition>\n";
            }
            let disk = '<Disk wcm:action="add">\n';
            disk += '    <CreatePartitions>\n';
            disk += CreatePartitions;
            disk += '    </CreatePartitions>\n';
            disk += '    <ModifyPartitions>\n';
            disk += ModifyPartitions;
            disk += '    </ModifyPartitions>\n';
            disk += '    <DiskID>'+ i +'</DiskID>\n';
            disk += '    <WillWipeDisk>true</WillWipeDisk>\n';
            disk += '</Disk>\n';
            _xml += disk;
        }
        _xml += WillShowUI;
        this.xml = _xml;
        return true;
    }

    this.refreshObj = function(_xml){
        var xml = _xml || this.xml
        if(!xml){
            this.error = false;
            this.xml = '';
            return true;
        }
        var obj = [];
        xml = xml.replace('<WillShowUI>OnError</WillShowUI>', '').replace("\r\n", "\n");
        if(xml.indexOf('<CreatePartitions>') == -1 || xml.indexOf('<ModifyPartitions>') == -1){
            this.error = true;
            return false;
        }
        var arr = xml.split("\n");
        for(let i = 0; i<arr.length; i++){
            let one = {
                "DiskID": 0,
                "WillWipeDisk": true, 
                "CreatePartitions": [], 
                "ModifyPartitions": [] 
            };
            if(arr[i].trim() == '<Disk wcm:action="add">'){
                for(let j = i+1; j<arr.length; j++){
                    if(arr[j].trim() == '</Disk>'){
                        obj.push(one);
                        i = j;
                        break;
                    }
                    var create_one = {};
                    if(arr[j].trim() == '<CreatePartition wcm:action="add">'){
                        for(let k = j+1; k<arr.length; k++){
                            if(arr[k].trim() == '</CreatePartition>'){
                                if(typeof create_one.Order == 'undefined' || typeof create_one.Type == 'undefined'){
                                    this.error = true;
                                    return false;
                                }
                                if(typeof create_one.Size == 'undefined' && typeof create_one.Extend == 'undefined'){
                                    this.error = true;
                                    return false;
                                }
                                if(typeof create_one.Size != 'undefined' && /[^\d]/.test(create_one.Size)){
                                    this.error = true;
                                    return false;
                                }
                                if(this.type == 'UEFI' && create_one.Order == 1 && create_one.Type != 'EFI'){
                                    this.error = true;
                                    return false;
                                }
                                one.CreatePartitions.push(create_one);
                                create_one = {};
                                continue;
                            }
                            if(arr[k].trim() == '<CreatePartition wcm:action="add">'){
                                continue;
                            }
                            if(arr[k].trim() == '</CreatePartitions>'){
                                j = k;
                                break;
                            }
                            let ta = arr[k].trim();
                            let s1 = ta.indexOf('<');
                            let s2 = ta.indexOf('>');
                            let s3 = ta.lastIndexOf('<')
                            let s4 = ta.lastIndexOf('>')
                            let key1 = ta.substr(s1+1, s2-s1-1)
                            let key2 = ta.substr(s3+1, s4-s3-1)
                            if(s1 != 0 || s4 != ta.length-1){
                                this.error = true;
                                return false;
                            }
                            if('/'+key1 != key2){
                                this.error = true;
                                return false;
                            }
                            create_one[key1] = ta.substr(s2+1, s3-s2-1);
                        }
                    }
                    if(arr[j].trim() == '<ModifyPartition wcm:action="add">'){
                        for(let k = j+1; k<arr.length; k++){
                            if(arr[k].trim() == '</ModifyPartition>'){
                                if(typeof create_one.Order == 'undefined' || typeof create_one.PartitionID == 'undefined' || (typeof create_one.Format != 'NTFS' && typeof create_one.Type == 'FAT32')){
                                    this.error = true;
                                    return false;
                                }
                                if(create_one.Order != create_one.PartitionID){
                                    this.error = true;
                                    return false;
                                }
                                one.ModifyPartitions.push(create_one);
                                create_one = {};
                                continue;
                            }
                            if(arr[k].trim() == '<ModifyPartition wcm:action="add">'){
                                continue;
                            }
                            if(arr[k].trim() == '</ModifyPartitions>'){
                                j = k;
                                break;
                            }
                            let ta = arr[k].trim();
                            let s1 = ta.indexOf('<');
                            let s2 = ta.indexOf('>');
                            let s3 = ta.lastIndexOf('<')
                            let s4 = ta.lastIndexOf('>')
                            let key1 = ta.substr(s1+1, s2-s1-1)
                            let key2 = ta.substr(s3+1, s4-s3-1)
                            if(s1 != 0 || s4 != ta.length-1){
                                this.error = true;
                                return false;
                            }
                            if('/'+key1 != key2){
                                this.error = true;
                                return false;
                            }
                            create_one[key1] = ta.substr(s2+1, s3-s2-1);
                        }
                    }
                    if(arr[j].indexOf('DiskID') != -1 || arr[j].indexOf('WillWipeDisk') != -1){
                        let ta = arr[j].trim();
                        let s1 = ta.indexOf('<');
                        let s2 = ta.indexOf('>');
                        let s3 = ta.lastIndexOf('<')
                        let s4 = ta.lastIndexOf('>')
                        let key1 = ta.substr(s1+1, s2-s1-1)
                        let key2 = ta.substr(s3+1, s4-s3-1)
                        if(s1 != 0 || s4 != ta.length-1){
                            this.error = true;
                            return false;
                        }
                        if('/'+key1 != key2){
                            this.error = true;
                            return false;
                        }
                        one[key1] = ta.substr(s2+1, s3-s2-1);
                    }
                }
                if(one.CreatePartitions.length != one.ModifyPartitions.length){
                    this.error = false;
                    return false;
                }
            }
        }
        this.obj = obj;
        if(xml && obj.length == 0){
            this.error = true;
        }else{
            this.error = false;
        }
        return true;
    }

    this.add = function(partition){
        let format = partition.format;
        let label = partition.label;
        let DiskID = partition.DiskID;
        let size = partition.size;

        if(this.obj.length == 0 || DiskID == 'new'){  //新磁盘,新分区
            if(this.type == 'UEFI'){  //UEFI自动添加order1
                var new_disk = {
                    "DiskID": this.obj.length,
                    "WillWipeDisk": "true", 
                    "CreatePartitions": [{"Order": 1, "Size": 200, "Type": "EFI"}], 
                    "ModifyPartitions": [{"Order": 1, "Format": "FAT32", "Label": "EFI", "PartitionID": 1}, {"Order": 2, "Format": format, "Label": label}]
                };
                if(size>0){
                    new_disk.CreatePartitions.push({"Order":2, "Size": size, "Type": "Primary"});
                }else{
                    new_disk.CreatePartitions.push({"Order":2, "Extend": "true", "Type": "Primary"});
                }
                this.obj.push(new_disk);
            }else{
                var new_disk = {
                    "DiskID": this.obj.length,
                    "WillWipeDisk": "true", 
                    "CreatePartitions": [], 
                    "ModifyPartitions": [{"Active": "true", "Order": 1, "Format": format, "Label": label}]
                };
                if(size>0){
                    new_disk.CreatePartitions.push({"Order":1, "Size": size, "Type": "Primary"});
                }else{
                    new_disk.CreatePartitions.push({"Order":1, "Extend": "true", "Type": "Primary"});
                }
                this.obj.push(new_disk);
            }
        }else{
            for(let i = 0; i<this.obj.length; i++){
                if(DiskID == this.obj[i].DiskID){
                    let order = this.obj[i].CreatePartitions.length + 1;
                    let last_key = this.obj[i].CreatePartitions.length - 1;
                    let has_extend = typeof this.obj[i].CreatePartitions[ last_key ].Extend != 'undefined';
                    if(size>0){
                        this.obj[i].CreatePartitions.push({"Order":order, "Size": size, "Type": "Primary"});
                        this.obj[i].ModifyPartitions.push({"Order": order, "Format": format, "Label": label});

                        if(has_extend){   //交换分区
                            let temp_order = this.obj[i].CreatePartitions[ last_key ].Order
                            let temp_create_partition = this.obj[i].CreatePartitions[ last_key ]
                            let temp_modify_partition = this.obj[i].ModifyPartitions[ last_key ]
                            this.obj[i].CreatePartitions[ last_key ] = this.obj[i].CreatePartitions[ last_key+1 ]
                            this.obj[i].CreatePartitions[ last_key+1 ] = temp_create_partition
                            this.obj[i].CreatePartitions[ last_key ].Order = temp_order
                            this.obj[i].CreatePartitions[ last_key+1 ].Order = order

                            this.obj[i].ModifyPartitions[ last_key ] = this.obj[i].ModifyPartitions[ last_key+1 ]
                            this.obj[i].ModifyPartitions[ last_key+1 ] = temp_modify_partition
                            this.obj[i].ModifyPartitions[ last_key ].Order = temp_order
                            this.obj[i].ModifyPartitions[ last_key+1 ].Order = order

                            if(last_key == 0){  //去掉active
                                this.obj[i].ModifyPartitions[ last_key ].Active = "true";
                                delete this.obj[i].ModifyPartitions[ last_key+1 ].Active;
                            }
                        }
                    }else{
                        if(has_extend){  //有2个剩余大小
                            this.error = true;
                            return false;
                        }else{
                            this.obj[i].CreatePartitions.push({"Order":order, "Extend": "true", "Type": "Primary"});
                            this.obj[i].ModifyPartitions.push({"Order": order, "Format": format, "Label": label});
                        }
                    }
                }
            }
        }
        this.refreshXml();
        this.error = false;
        return true;
    }

    this.modify = function(partition){
        let DiskID = partition.DiskID;
        let Order = partition.Order;

        let format = partition.format;
        let label = partition.label;
        let size = partition.size;

        if(this.type == 'UEFI' && Order == 1){
            return false;
        }
        for(let i = 0; i<this.obj.length; i++){
            if(this.obj[i].DiskID == DiskID){
                for(let j = 0; j<this.obj[i].CreatePartitions.length; j++){
                    if(this.obj[i].CreatePartitions[j].Order == Order){
                        if(size > 0){
                            this.obj[i].CreatePartitions[j].Size = size;
                        }else{  //修改为剩余只能是最后一个分区
                            if(j != this.obj[i].CreatePartitions.length - 1){
                                this.error = true;
                                return false;
                            }else{
                                delete this.obj[i].CreatePartitions[j].Size
                                this.obj[i].CreatePartitions[j].Extend = "true"
                            }
                        }
                        break;
                    }
                }
                for(let j = 0; j<this.obj[i].ModifyPartitions.length; j++){
                    if(this.obj[i].ModifyPartitions[j].Order == Order){
                        this.obj[i].ModifyPartitions[j].Format = format 
                        this.obj[i].ModifyPartitions[j].Label = label
                        break;
                    }
                }
            }
        }
        this.error = false;
        this.refreshXml();
        return true;
    }

    this.delete = function(DiskID, Order){
        let delete_disk = false;
        for(let i = 0; i<this.obj.length; i++){
            if(DiskID == this.obj[i].DiskID){
                if(this.obj[i].CreatePartitions.length == 1){ //只有一个分区,直接删除
                    delete_disk = true;
                    this.obj.splice(i, 1);
                }else{
                    if(this.type == 'UEFI' && Order == 1){
                        break;
                    }
                    let delete_target_create = -1;
                    let delete_target_modify = -1;
                    for(let j = 0; j<this.obj[i].CreatePartitions.length; j++){
                        if(delete_target_create == -1 && Order == this.obj[i].CreatePartitions[j].Order){
                            delete_target_create = j;
                        }
                        if(Order < this.obj[i].CreatePartitions[j].Order){ 
                            this.obj[i].CreatePartitions[j].Order--;
                        }
                    }
                    for(let j = 0; j<this.obj[i].ModifyPartitions.length; j++){
                        if(delete_target_modify == -1 && Order == this.obj[i].ModifyPartitions[j].Order){
                            delete_target_modify = j;
                        }
                        if(Order < this.obj[i].ModifyPartitions[j].Order){ 
                            this.obj[i].ModifyPartitions[j].Order--;
                            this.obj[i].ModifyPartitions[j].PartitionID--;
                        }
                    }
                    if(delete_target_create != -1){
                        this.obj[i].CreatePartitions.splice(delete_target_create, 1);
                    }
                    if(delete_target_modify != -1){
                        this.obj[i].ModifyPartitions.splice(delete_target_modify, 1);
                        if(Order == 1){  //删除的第一个order,重新添加Active
                            this.obj[i].ModifyPartitions[0].Active = "true";
                        }
                    }
                }
                break;
            }
        }
        if(delete_disk){
            for(let i = 0; i<this.obj.length; i++){
                if(DiskID < this.obj[i].DiskID){
                    this.obj[i].DiskID--;   
                }
            }
        }
        this.refreshXml();
        this.error = false;
        return true;
    }

    this.reset = function(){
        this.xml = this.origin
        this.refreshObj()
        return true;
    }

    this.changeType = function(){
        if(this.autopart) return this;
        if(this.type == 'BIOS'){ 
            for(let i = 0; i<this.obj.length; i++){
                if(this.obj[i].CreatePartitions[0].Type == 'EFI'){
                    continue;
                }
                for(let j = 0; j<this.obj[i].CreatePartitions.length; j++){
                    this.obj[i].CreatePartitions[j].Order++;
                }
                for(let j = 0; j<this.obj[i].ModifyPartitions.length; j++){
                    this.obj[i].ModifyPartitions[j].Order++;
                    this.obj[i].ModifyPartitions[j].PartitionID++;
                }
                this.obj[i].CreatePartitions.unshift({Order: "1", Size: "200", Type: "EFI"});
                this.obj[i].ModifyPartitions.unshift({Order: "1", Format: "FAT32", Label: "EFI", PartitionID: "1"});
            }
            this.type = 'UEFI';
        }else{
            for(let i = 0; i<this.obj.length; i++){
                if(this.obj[i].CreatePartitions[0].Type != 'EFI'){
                    continue;
                }
                this.obj[i].CreatePartitions.shift();
                this.obj[i].ModifyPartitions.shift();
                for(let j = 0; j<this.obj[i].CreatePartitions.length; j++){
                    this.obj[i].CreatePartitions[j].Order--;
                }
                for(let j = 0; j<this.obj[i].ModifyPartitions.length; j++){
                    this.obj[i].ModifyPartitions[j].Order--;
                    this.obj[i].ModifyPartitions[j].PartitionID--;
                }
            }
            this.type = 'BIOS';
        }
        return this;
    }

    this.setVersion = function(version){
        this.version = version;
        return this
    }

}

var Centos_Partition = function(s, type){
    this.error = false;
    this.origin = s;
    this.obj = [];
    this.xml = '';
    this.type = type || 'BIOS';
    this.os = 'centos';
    this.autopart = false;
    this.parts = [];  //已有分区
    this.selected_fs = ['ext4', 'ext3', 'ext2', 'swap', 'xfs', 'biosboot'];
    this.auto_xml = [
        "autopart"
    ];
    this.version = '';

    this.init = function(){
        this.refreshObj(this.origin);
        this.refreshXml();
        return this;
    }

    this.refreshXml = function(_obj){
        var obj = _obj || this.obj
        if(obj.length == 0){
            this.xml = '';
            this.parts = [];
            return true;
        }
        var xml = '';
        var parts = [];
        for(let i = 0; i<obj.length; i++){
            parts.push(obj[i].part);
            xml += "part " + obj[i].part + " --fstype=" + obj[i].fs;
            if(obj[i].size>0){
                xml += " --size=" + obj[i].size;
            }
            if(obj[i].grow){
                xml += " --size=100";
                xml += " --grow";
            }
            if(obj[i].left.length > 0){
                xml += " " + obj[i].left;
            }
            xml += "\n";
        }
        this.parts = parts;
        this.xml = xml.trim();
        return true;
    }

    this.refreshObj = function(command){
        var cmd = command || this.xml;
        if(!cmd){
            this.obj = [];
            this.autopart = false;
            this.error = false;
            this.xml = '';
            this.parts = [];
            return true;
        }
        if(command.trim() == 'autopart'){
            this.obj = [];
            this.autopart = true;
            this.error = false;
            this.parts = [];
            this.xml = '';
            return true;
        }
        var arr = cmd.trim().split("\n");
        var parts = [];
        var obj = [];
        for(let i = 0; i<arr.length; i++){
            let a = arr[i].match(/part ([^ ]+)/)
            let b = arr[i].match(/--fstype( |=)([^ ]+)/)
            let c = arr[i].match(/--size=([^ ]+)/)
            let d = arr[i].match(/--grow/)
            if(!a || !b){
                this.error = true;
                return false;
            }
            let create_one = {}
            create_one.part = a[1].replace(/'|"/g, '');
            create_one.fs = b[2].replace(/'|"/g, '');
            create_one.size = c ? c[1].replace(/'|"/g, '') : 0;
            create_one.grow = d ? true : false;
            if(create_one.grow){
                create_one.size = 0;
            }
            if(create_one.part.indexOf('/') == -1 && create_one.part != 'swap' && create_one.part != 'biosboot'){  //分区格式错误
                this.error = true;
                return false;
            }
            if(this.type == 'UEFI' && i == 0 && (create_one.part != '/boot/efi' || create_one.fs != 'efi')){ //UEFI错误
                this.error = true;
                return false;
            } 
            if(this.selected_fs.indexOf(create_one.fs) == -1){ //fs格式错误
                if(this.type == 'UEFI' && i == 0 && create_one.fs == 'efi'){

                }else{
                    this.error = true;
                    return false;
                }
            }
            if(/[^\d]/.test(create_one.size)){  //size错误
                this.error = true;
                return false;
            }
            if(create_one.size == 0 && !create_one.grow){  //没有大小就需要剩余
                this.error = true;
                return false;
            }
            if(i != arr.length-1 && create_one.grow){  //只能最后是剩余
                this.error = true;
                return false;
            }
            let left = arr[i].replace(a[0], '').replace(b[0], '')
            if(c) left = left.replace(c[0], '');
            if(d) left = left.replace(d[0], '');
            left = left.trim()
            create_one.left = left
            if(parts.indexOf(create_one.part) != -1){  //分区重复
                this.error = true;
                return false;
            }
            parts.push(create_one.part)
            obj.push(create_one);
        }
        this.autopart = false;
        this.parts = parts;
        this.obj = obj;
        this.error = false;
        return true;
    }

    this.add = function(partition){
        let part = partition.part
        let size = partition.size
        let fs = partition.fs

        if(!part || this.parts.indexOf(part) != -1){
            this.error = true;
            return false;
        }
        if(!fs || this.selected_fs.indexOf(fs) == -1){
            this.error = true;
            return false;
        }
        if(size && /[^\d]/.test(size)){
            this.error = true;
            return false;
        }
        if(this.obj.length > 0 && this.obj[ this.obj.length - 1 ].grow && !size){
            this.error = true;
            return false;
        }
        if(this.type == 'UEFI' && this.obj.length == 0){ //自动添加uefi
            this.obj.push({"part": "/boot/efi", "fs": "efi", "size": 500, "grow": false, "left": ""});
        }
        let create_one = {};
        create_one.part = part;
        create_one.fs = fs;
        create_one.size = +size;
        create_one.left = "";
        create_one.grow = size>0 ? false : true;
        this.obj.push(create_one);
        this.refreshXml();
        this.error = false;
        return true;
    }

    this.modify = function(partition){
        let row = partition.row;
        let size = partition.size;
        let part = partition.part;
        let fs = partition.fs;

        if(size && /[^\d]/.test(size)){
            this.error = true;
            return false;
        }
        if(!size && row != this.obj.length - 1){
            this.error = true;
            return false;
        }
        if(row >= this.obj.length){
            this.error = true;
            return false;
        }
        if(this.type == 'UEFI' && row == 0){
            this.obj[row].size = +size;
            this.obj[row].left = size>0 ? false : true;
            this.refreshXml();
            this.error = false;
            return true;
        }else{
            if(!part || (this.parts.indexOf(part) != row && this.parts.indexOf(part) != -1)){
                this.error = true;
                return false;
            }
            if(!fs || this.selected_fs.indexOf(fs) == -1){
                this.error = true;
                return false;
            }
            this.obj[row].part = part;
            this.obj[row].fs = fs;
            this.obj[row].size = +size;
            this.obj[row].grow = size>0 ? false : true;
            this.refreshXml();
            this.error = false;
            return true;
        }
    }

    this.delete = function(row){
        if(row >= this.obj.length){
            this.error = true;
            return false;
        }
        if(this.obj.length > 1 && this.type == 'UEFI' && row == 0){
            this.error = true;
            return false;
        }
        this.obj.splice(row, 1);
        this.refreshXml();
        this.error = false;
        return true;
    }

    this.reset = function(){
        this.xml = this.origin
        this.refreshObj()
        return true;
    }

    this.setAuto = function(){
        this.xml = '';
        this.obj = [];
        this.autopart = true;
        this.error = false;
        return true;
    }

    this.cancelAuto = function(){
        this.xml = '';
        this.obj = [];
        this.autopart = false;
        this.error = false;
        return true;
    }

    this.changeType = function(){
        if(this.autopart) return this;
        if(this.type == 'BIOS'){
            if(this.obj[0].fs != 'efi'){
                this.obj.unshift({part: "/boot/efi", fs: "efi", size: "500", grow: false, left: ""});
            }
            this.type = 'UEFI';
        }else{
            if(this.obj[0].fs == 'efi'){
                this.obj.shift();
            }
            this.type = 'BIOS';
        }
        return this;
    }

    this.setVersion = function(version){
        this.version = version;
        return this
    }
}

var Ubuntu_Partition = function(s, type, os){
    this.error = false;
    this.origin = s;
    this.obj = [];
    this.xml = '';
    this.type = type || 'BIOS';
    this.os = os || 'ubuntu';
    this.autopart = false;
    this.parts = [];  //已有分区
    this.selected_fs = ['ext4', 'ext3', 'ext2', 'swap', 'xfs', 'biosboot'];
    this.max_priority = 0;
    this.left = 10000000000;  //剩余界定值
    this.version = '';
    this.auto_xml = [
        'd-i partman/early_command string \\',
        'debconf-set partman-auto/disk "$(list-devices disk | head -n1)"; \\',
        'debconf-set grub-installer/bootdev "$(list-devices disk | head -n1)"; \\',
        'vgremove -ff `vgs |sed -n \'2p\' |cut -d " " -f 3`; \\',
        'pvremove -y -ff `list-devices disk | head -n1`* || true',
        'd-i partman-auto/choose_recipe select atomic',
        'd-i partman-auto/method string lvm',
        'd-i partman-lvm/confirm boolean true',
        'd-i partman-lvm/device_remove_lvm boolean true',
        'd-i partman-lvm/confirm_nooverwrite boolean true',
        'd-i partman-md/device_remove_md boolean true',
        'd-i partman-partitioning/confirm_write_new_label boolean true',
        'd-i partman/choose_partition select finish',
        'd-i partman/confirm boolean true',
        'd-i partman/confirm_nooverwrite boolean true',
        /*
        'd-i partman/early_command string \\',
        'debconf-set partman-auto/disk "$(list-devices disk | head -n1)"; \\',
        'debconf-set grub-installer/bootdev "$(list-devices disk | head -n1)" ; \\',
        'pvremove -y -ff `list-devices disk | head -n1`* || true',
        'd-i partman-auto/choose_recipe select atomic',
        //'d-i partman-auto/disk string /dev/sda',
        'd-i partman-auto/method string lvm',
        'd-i partman-lvm/confirm boolean true',
        'd-i partman-lvm/confirm_nooverwrite boolean true',
        'd-i partman-lvm/device_remove_lvm boolean true',
        'd-i partman-md/device_remove_md boolean true',
        'd-i partman-partitioning/confirm_write_new_label boolean true',
        'd-i partman/choose_partition select finish',
        'd-i partman/confirm boolean true',
        'd-i partman/confirm_nooverwrite boolean true',
        */
    ];
    this.debian_auto_xml = [
        'd-i partman/early_command string \\',
        'list-devices disk 2>&1|cut -d\':\' -f1 |sed \'s/ //g\' |sort |uniq -c|grep 1 |sed \'s/.*1 //g\' > /run/disk; \\',
        'debconf-set partman-auto/disk "$(cat /run/disk | head -n1)"; \\',
        'debconf-set grub-installer/bootdev "$(cat /run/disk | head -n1)" ; \\',
        'pvremove -y -ff `cat /run/disk | head -n1`* || true',
        'd-i partman-auto/method string regular',
        'd-i partman-lvm/device_remove_lvm boolean true',
        'd-i partman-md/device_remove_md boolean true',
        'd-i partman-auto/expert_recipe string boot-root :: \\',
        '       200 50 200 xfs \\',
        '       $primary{ } \\',
        '       $bootable{ } \\',
        '       method{ format } \\',
        '       format{ } \\',
        '       use_filesystem{ } \\',
        '       filesystem{ xfs } \\',
        '       mountpoint{ /boot } \\',
        '       . \\',
        '       51200 60 51200 ext4 \\',
        '       $primary{ } \\',
        '       method{ format } \\',
        '       format{ } \\',
        '       use_filesystem{ } \\',
        '       filesystem{ ext4 } \\',
        '       mountpoint{ / } \\',
        '       . \\',
        '       8192 70 8192 linux-swap \\',
        '       method{ swap } \\',
        '       format{ } \\',
        '       . \\',
        '       100 100000 10000000000 ext4 \\',
        '       $primary{ } \\',
        '       method{ format } \\',
        '       format{ } \\',
        '       use_filesystem{ } \\',
        '       filesystem{ ext4 } \\',
        '       mountpoint{ /home } \\',
        '       .',
        'd-i partman-lvm/confirm boolean true',
        'd-i partman-lvm/device_remove_lvm boolean true',
        'd-i partman-md/confirm_nooverwrite boolean true',
        'd-i partman/choose_partition select finish',
        'd-i partman/confirm_nooverwrite boolean true',
        'd-i partman/confirm boolean true',
        'd-i partman/confirm_write_new_label boolean true',
        'd-i partman/confirm_write_changes_to_disks_and_configure_lvm boolean true',
        'd-i partman/choose_partition select finish',
        'd-i partman-md/confirm boolean true',
        'd-i partman-partitioning/confirm_write_new_label boolean true'
    ];
    this.header = [
        'd-i partman/early_command string \\',
        'debconf-set partman-auto/disk "$(list-devices disk | head -n1)"; \\',
        'debconf-set grub-installer/bootdev "$(list-devices disk | head -n1)" ; \\',
        'pvremove -y -ff `list-devices disk | head -n1`* || true',
        //'d-i partman-auto/disk string /dev/sda',
        'd-i partman-auto/method string regular',
        'd-i partman-lvm/device_remove_lvm boolean true',
        'd-i partman-md/device_remove_md boolean true'
    ];
    this.footer = [
        'd-i partman-auto/choose_recipe select boot-root',
        'd-i partman-partitioning/confirm_write_new_label boolean true',
        'd-i partman/choose_partition select Finish partitioning and write changes to disk',
        'd-i partman/confirm_nooverwrite boolean true',
        'd-i partman/confirm boolean true',
        'd-i partman/mount_style select uuid',
        'd-i partman/choose_partition select finish'
    ];
    this.debian_header = [
        'd-i partman/early_command string \\',
        'list-devices disk 2>&1|cut -d\':\' -f1 |sed \'s/ //g\' |sort |uniq -c|grep 1 |sed \'s/.*1 //g\' > /run/disk; \\',
        'debconf-set partman-auto/disk "$(cat /run/disk | head -n1)"; \\',
        'debconf-set grub-installer/bootdev "$(cat /run/disk | head -n1)" ; \\',
        'pvremove -y -ff `cat /run/disk | head -n1`* || true',
        //'d-i partman-auto/disk string /dev/sda',
        'd-i partman-auto/method string regular',
        'd-i partman-lvm/device_remove_lvm boolean true',
        'd-i partman-md/device_remove_md boolean true'
    ];
    this.debian_footer = [
        'd-i partman-lvm/confirm boolean true',
        'd-i partman-lvm/device_remove_lvm boolean true',
        'd-i partman-md/confirm_nooverwrite boolean true',
        'd-i partman/choose_partition select finish',
        'd-i partman/confirm_nooverwrite boolean true',
        'd-i partman/confirm boolean true',
        'd-i partman/confirm_write_new_label boolean true',
        'd-i partman/confirm_write_changes_to_disks_and_configure_lvm boolean true',
        'd-i partman/choose_partition select finish',
        'd-i partman-md/confirm boolean true',
        'd-i partman-partitioning/confirm_write_new_label boolean true'
    ];
    this.ubuntu20_auto_xml = [
        '  storage:',    
        '    layout:',    
        '      name: lvm',
    ];

    this.init = function(){
        this.refreshObj(this.origin);
        this.refreshXml();
        return this;
    }

    this.refreshXml = function(_obj){
        if(this.os == 'ubuntu' && this.version == 20){
            return this.refreshXml20(_obj);
        }
        var obj = _obj || this.obj
        if(obj.length == 0){
            this.xml = '';
            this.parts = [];
            this.max_priority = 0;
            this.error = false;
            return true;
        }
        if(this.autopart){
            this.xml = '';
            this.parts = [];
            this.max_priority = 0;
            this.error = false;
            return true;
        }
        var xml = '';
        if(this.os == 'debian'){
            xml += this.debian_header.join("\n") 
        }else{
            xml += this.header.join("\n") 
        }
        xml += "\n";
        xml += "d-i partman-auto/expert_recipe string boot-root :: \\\n";
        var parts = [];
        var max_priority = 0;
        for(let i = 0; i<this.obj.length; i++){
            if(obj[i].size == 0 || obj[i].size == this.left){
                obj[i].min_size = 100;
                obj[i].priority = 100000;
                obj[i].size = this.left;
            }else{
                obj[i].min_size = obj[i].size;
            }
            if(obj[i].priority > max_priority){
                max_priority = obj[i].priority;
            }
            if(obj[i].method == 'swap'){
                xml += "        " + obj[i].min_size + " " + obj[i].priority + " " + obj[i].size + " linux-swap \\\n";
                xml += "        method{ swap } \\\n";
                xml += "        format{ } \\\n";
                parts.push('swap');
            }else if(obj[i].method == 'biosboot'){
                xml += "        " + obj[i].min_size + " " + obj[i].priority + " " + obj[i].size + " linux-biosboot \\\n";
                xml += "        method{ biosboot } \\\n";
                xml += "        format{ } \\\n";
                parts.push('biosboot');
            }else{
                xml += "        " + obj[i].min_size + " " + obj[i].priority + " " + obj[i].size + " " + obj[i].fs + " \\\n";
                if(obj[i].primary){
                    xml += "        $primary{ } \\\n";
                }
                if(obj[i].bootable){
                    xml += "        $bootable{ } \\\n";
                }
                xml += "        method{ " + obj[i].method + " } \\\n";
                if(obj[i].format){
                    xml += "        format{ } \\\n";
                }
                if(obj[i].use_filesystem){
                    xml += "        use_filesystem{ } \\\n";
                }
                if(obj[i].fs){
                    xml += "        filesystem{ " + obj[i].fs + " } \\\n";
                }
                if(obj[i].mountpoint){
                    xml += "        mountpoint{ " + obj[i].mountpoint + " } \\\n";
                }
                parts.push(obj[i].mountpoint);
            }
            if(i == this.obj.length-1){
                xml += "        . \n";
            }else{
                xml += "        . \\\n";
            }
        }
        if(this.os == 'debian'){
            xml += this.debian_footer.join("\n");
        }else{
            xml += this.footer.join("\n");
        }
        this.xml = xml;
        this.parts = parts;
        this.max_priority = max_priority;
        return true;
    }

    this.refreshXml20 = function(_obj){
        var obj = _obj || this.obj
        if(obj.length == 0){
            this.xml = '';
            this.parts = [];
            this.max_priority = 0;
            this.error = false;
            return true;
        }
        if(this.autopart){
            this.xml = '';
            this.parts = [];
            this.max_priority = 0;
            this.error = false;
            return true;
        }
        var xml = '';
        var parts = [];
        xml += "storage:\n"
    +"  config:\n"
    +"  - {ptable: gpt, path: /dev/sda, wipe: superblock-recursive, preserve: false, name: '', grub_device: true, type: disk, id: disk-sda}\n"
    +"  - {device: disk-sda, size: 1048576, flag: bios_grub, number: 1, preserve: false, type: partition, id: partition-2}\n";        
        for(let i = 0; i<this.obj.length; i++){
            parts.push(this.obj[i].mountpoint)
            xml += "  - {device: disk-sda, size: " + (this.obj[i].size == 0 ? -1 : this.obj[i].size*1024*1024) + ", wipe: superblock, flag: " + (this.obj[i].part == 'swap' || this.obj[i].part == 'biosboot' ? this.obj[i].part : "''") + ", number: "+(i+2)+", preserve: false, type: partition, id: partition-"+(i+3)+"}\n"
                +  "  - {fstype: " + this.obj[i].fs + ", volume: partition-"+(i+3)+", preserve: false, type: format, id: format-"+(i+1)+"}\n"
                +  "  - {device: format-" + (i+1) + ", path: " + (this.obj[i].part == 'swap' || this.obj[i].part == 'biosboot' ? "''" : this.obj[i].part) + ", type: mount, id: mount-"+(i+1)+"}\n";
        }
        //if(parts.indexOf('swap') != -1){
            xml += "  swap: {swap: 0}";
        //}
        this.parts = parts
        this.xml = xml;
        return true;
    }

    this.refreshObj = function(command){
        if(this.os == 'ubuntu' && this.version == 20){
            return this.refreshObj20(command);
        }
        if(typeof command != 'undefined'){
            command = command.trim()
        }
        var cmd = command || this.xml;
        if(!cmd){
            this.obj = [];
            this.autopart = false;
            this.error = false;
            this.xml = '';
            this.parts = [];
            return true;
        }
        if(this.checkAuto(cmd)) return true;
        var mo = cmd.match(/\d+ ([^.]+).( ?\\?)?/g);
        if(!mo){
            this.error = true;
            this.xml = '';
            return false;
        }
        for(let i = 0; i<mo.length; i++){
            cmd = cmd.replace(mo[i], '');
        }
        var arr = cmd.split("\n")
        var target = 0;
        for(let i = 0; i<arr.length; i++){
            if(arr[i].indexOf('boot-root') != -1){
                target = i;
                break;
            }
        }
        arr.splice(0, target);
        //验证d-i行是否正确
        // var header = arr.splice(0, 8);
        // header.sort()
        // for(let i = 0; i<header.length; i++){
        //     if(header[i].trim() != this.header[i]){
        //         this.error = true;
        //         return false;
        //     }
        // }
        if(arr[1].trim().length > 0){
            if(arr[0] != 'd-i partman-auto/expert_recipe string \\'){
                this.error = true;
                return false;
            }
            var r6 = arr.splice(1, 1);
            if(r6[0].trim() != 'boot-root :: \\'){
                this.error = true;
                return false;
            }
            arr.splice(0, 1);
        }else{
            if(arr[0].trim() != 'd-i partman-auto/expert_recipe string boot-root :: \\'){
                this.error = true;
                return false;
            }
            arr.splice(0, 1);
        }
        var footer = [];
        for(let i = 0; i<arr.length; i++){
            if(arr[i].trim()){
                footer.push(arr[i].trim());
            }
        }
        if(this.os == 'debian'){
            if(footer.length == 0 || footer.length != this.debian_footer.length){
                this.error = true;
                return false;
            }
            for(let i = 0; i<footer.length; i++){
                if(footer[i] != this.debian_footer[i]){
                    this.error = true;
                    return false;
                }
            }
        }else{
            if(footer.length == 0 || footer.length != this.footer.length){
                this.error = true;
                return false;
            }
            for(let i = 0; i<footer.length; i++){
                if(footer[i] != this.footer[i]){
                    this.error = true;
                    return false;
                }
            }
        }
        //解析主要数据
        var parts = [];
        var obj = [];
        for(let i = 0; i<mo.length; i++){
            let create_one = {}
            let arr_mo = mo[i].split("\n")
            for(let j = 0; j<arr_mo.length; j++){
                let t = arr_mo[j].trim()
                let last = t.indexOf('\\')
                if(i == mo.length - 1 && j == arr_mo.length - 1){
                    if(last != -1){
                        this.error = true;
                        return false;
                    }
                }else{
                    if(last == -1 || last != t.length - 1){
                        this.error = 2;
                        return false;
                    }
                }
                arr_mo[j] = t.replace('\\', '')
            }
            let one = arr_mo.join('')
            let a = one.match(/(\d+) (\d+) (\d+) ([a-z0-9\-]+)/)
            if(!a){
                this.error = true;
                return false;
            }
            let primary = one.match(/ \$primary{([^}]+)}/);
            let bootable = one.match(/ \$bootable{([^}]+)}/);
            let method = one.match(/ method{([^}]+)}/);
            let format = one.match(/ format{([^}]+)}/);
            let use_filesystem = one.match(/ use_filesystem{([^}]+)}/);
            let filesystem = one.match(/ filesystem{([^}]+)}/);
            let mountpoint = one.match(/ mountpoint{([^}]+)}/);
            //$primary{ } $bootable{ } method{ format } format{ } use_filesystem{ } filesystem{ xfs } mountpoint{ /boot } . 
            create_one.min_size = +a[1];
            create_one.priority = +a[2];  //优先级
            create_one.size = +a[3];
            create_one.primary = primary ? true : false;
            create_one.bootable = bootable ? true : false;
            create_one.method = method ? method[1].trim() : null;
            create_one.format = format ? true : false;
            create_one.use_filesystem = use_filesystem ? true : false;
            create_one.fs = filesystem ? filesystem[1].trim() : null;
            create_one.mountpoint = mountpoint ? mountpoint[1].trim() : null;
            if(create_one.method != 'swap' && create_one.method != 'biosboot' && !create_one.mountpoint){
                this.error = true;
                return false;
            }
            if(create_one.method == 'swap'){
                if(a[4] != 'linux-swap'){
                    this.error = true;
                    return false;
                }
                create_one.fs = "swap";
            }else if(create_one.method == 'biosboot'){
                if(a[4] != 'linux-biosboot'){
                    this.error = true;
                    return false;
                }
                create_one.fs = "biosboot";
            }else{
                if(a[4] == 'linux-swap' || a[4] == 'linux-biosboot' || create_one.fs != a[4]){
                    this.error = true;
                    return false;
                }
            }
            if(create_one.mountpoint == '/boot' && !create_one.bootable){
                this.error = true;
                //return false;
            }
            if(create_one.mountpoint != '/boot' && create_one.bootable){
                this.error = true;
                return false;
            }
            if(create_one.fs != 'swap' && create_one.fs != 'biosboot' && !create_one.use_filesystem){
                this.error = true;
                return false;
            }
            create_one.part = create_one.mountpoint;
            if(create_one.method == 'swap'){
                create_one.part = "swap";
            }else if(create_one.method == 'biosboot'){
                create_one.part = "biosboot";
            }
            if(parts.indexOf(create_one.part) != -1){
                this.error = true;
                return false;
            }
            if(create_one.priority > this.max_priority){
                this.max_priority = create_one.priority;
            }
            parts.push(create_one.part)
            obj.push(create_one)
        }
        obj.sort(function(a, b){
            return a.priority - b.priority;
        });
        this.parts = parts;
        this.obj = obj;
        this.autopart = false;
        this.error = false;
        return true;
    }

    this.refreshObj20 = function(command){
        if(typeof command != 'undefined'){
            command = command.trim()
        }
        var cmd = command || this.xml;
        if(!cmd){
            this.obj = [];
            this.autopart = false;
            this.error = false;
            this.xml = '';
            this.parts = [];
            return true;
        }
        if(this.checkAuto(cmd)) return true;
        var mo = cmd.match(/{[^}]+}/g);
        if(!mo){
            this.error = true;
            this.xml = '';
            return false;
        }
        var arr = []
        for(let i = 0; i<mo.length; i++){
            if(i == 0 || i == 1){
                continue;
            }
            let tmp = mo[i].toString().split(',')
            arr[i] = {}
            for(let j = 0; j<tmp.length; j++){
                tmp[j] = tmp[j].replace('{', '')
                tmp[j] = tmp[j].replace('}', '')
                tmp[j] = tmp[j].trim()
                tmp[j] = tmp[j].split(':')
                // arr[i] = {}
                arr[i][tmp[j][0].trim()] = tmp[j][1].replace(/'/g, '').trim()
            }
        }
        var parts = [];
        var obj = [];
        for(let i = 0; i<arr.length; i++){
            if(i == 0 || i == 1 || typeof arr[i+2] == 'undefined'){
                continue;
            }
            let create_one = {
                bootable: false,
                fs: "",
                format: true,
                method: "format",
                min_size: 500,
                mountpoint: null,
                primary: false,
                priority: 1,
                size: arr[i].size == -1 ? 0 : +arr[i].size/1024/1024,
                use_filesystem: false,
                part: ""
            }
            if(arr[i].id == arr[i+1].volume){
                create_one.fs = arr[i+1].fstype;
                if(arr[i+1].id == arr[i+2].device){
                    create_one.mountpoint = arr[i+2].path;
                    create_one.part = arr[i+2].path;
                    i += 2;
                }
            }
            if(create_one.fs == 'swap' || create_one.fs == 'biosboot'){
                create_one.method = create_one.fs
                create_one.part = create_one.fs
            }
            parts.push(create_one.part)
            obj.push(create_one)
        }
        this.parts = parts;
        this.obj = obj;
        this.autopart = false;
        this.error = false;
        return true;
    }

    this.add = function(partition){
        let part = partition.part;
        let fs = partition.fs;
        let size = partition.size;

        if(this.parts.indexOf(part) != -1){
            this.error = true;
            return false;
        }
        if(this.selected_fs.indexOf(fs) == -1){
            this.error = true;
            return false;
        }
        var has_left = this.obj.length>0 && (this.obj[this.obj.length-1].size == 0 || this.obj[this.obj.length-1].size >= this.left);
        if((size == 0 || size >= this.left) && has_left){
            this.error = true;
            return false;
        }
        if(part == 'swap'){
            var create_one = {
                bootable: false,
                fs: "swap",
                format: true,
                method: "swap",
                min_size: 500,
                mountpoint: null,
                primary: false,
                priority: this.max_priority + 1,
                size: size,
                use_filesystem: false,
                part: "swap"
            }
        }else if(part == 'biosboot'){
            var create_one = {
                bootable: false,
                fs: "biosboot",
                format: true,
                method: "biosboot",
                min_size: 500,
                mountpoint: null,
                primary: false,
                priority: this.max_priority + 1,
                size: size,
                use_filesystem: false,
                part: "biosboot"
            }
        }else{
            var create_one = {}
            create_one.bootable = false;
            create_one.fs = fs;
            create_one.format = true;
            create_one.method = "format";
            create_one.min_size = 500;
            create_one.mountpoint = part;
            create_one.primary = true;
            create_one.priority = this.max_priority + 1;
            create_one.size = size;
            create_one.use_filesystem = true;
            if(part == '/boot'){
                create_one.bootable = true;
            }
            create_one.part = part;
        }
        this.obj.push(create_one);
        this.parts.push(create_one.part);
        if(size >0 && size<this.left && has_left){
            let origin_last_one = this.obj[this.obj.length-2].priority
            this.obj[this.obj.length-2].priority = create_one.priority
            this.obj[this.obj.length-1].priority = origin_last_one
            this.obj.sort(function(a, b){
                return a.priority - b.priority;
            });
        }
        this.max_priority++;
        this.refreshXml();
        this.error = false;
        return true;
    }

    this.modify = function(partition){
        let row = partition.row;
        let part = partition.part;
        let fs = partition.fs;
        let size = partition.size;

        if(this.parts.indexOf(part) != row && this.parts.indexOf(part) != -1){
            this.error = true;
            return false;
        }
        if(this.selected_fs.indexOf(fs) == -1){
            this.error = true;
            return false;
        }
        if((size == 0 || size >= this.left) && row != this.obj.length-1){
            this.error = true;
            return false;
        }
        if(row >= this.obj.length){
            return false;
        }
        if(part == 'swap'){
            this.obj[row].bootable = false;
            this.obj[row].fs = "swap";
            this.obj[row].format = true;
            this.obj[row].method = "swap";
            this.obj[row].mountpoint = null;
            this.obj[row].primary = false;
            this.obj[row].size = size;
            this.obj[row].use_filesystem = false;
            this.obj[row].part = "swap";
        }else if(part == 'biosboot'){
            this.obj[row].bootable = false;
            this.obj[row].fs = "biosboot";
            this.obj[row].format = true;
            this.obj[row].method = "biosboot";
            this.obj[row].mountpoint = null;
            this.obj[row].primary = false;
            this.obj[row].size = size;
            this.obj[row].use_filesystem = false;
            this.obj[row].part = "biosboot";
        }else{
            this.obj[row].bootable = false;
            this.obj[row].fs = fs;
            this.obj[row].format = true;
            this.obj[row].method = "format";
            this.obj[row].mountpoint = part;
            this.obj[row].primary = true;
            this.obj[row].size = size;
            this.obj[row].use_filesystem = true;
            if(part == '/boot'){
                this.obj[row].bootable = true;
            }
            this.obj[row].part = part;
        }
        this.refreshXml();
        this.error = false;
        return true;
    }

    this.delete = function(row){
        if(row >= this.obj.length){
            this.error = true;
            return false;
        }
        if(this.obj.length > 1 && this.type == 'UEFI' && row == 0){
            this.error = true;
            return false;
        }
        this.obj.splice(row, 1);
        this.refreshXml();
        this.error = false;
        return true;
    }

    this.checkAuto = function(command){
        var cmd = command || this.xml;
        if(this.os == 'debian'){
            if(cmd == this.debian_auto_xml.join("\n")){
                this.autopart = true;
                this.error = false;
                this.parts = [];
                this.xml = '';
                return true;
            }else{
                return false;
            }
        }else{
            if(this.os == 'ubuntu' && this.version == 20){
                if(cmd.indexOf('storage:') != -1 && cmd.indexOf('layout:') != -1 && cmd.indexOf('name: lvm') != -1){
                    this.autopart = true;
                    this.error = false;
                    this.parts = [];
                    this.xml = '';
                    return true;
                }else{
                    return false;
                }
            }
        }
        if(cmd.indexOf('boot-root') != -1){
            return false;
        }
        this.autopart = true;
        this.error = false;
        this.parts = [];
        this.xml = '';
        return true;
    }

    this.setAuto = function(){
        this.xml = '';
        this.obj = [];
        this.autopart = true;
        this.error = false;
        return true;
    }

    this.cancelAuto = function(){
        this.autopart = false;
        this.obj = [];
        this.xml = '';
        this.error = false;
        return true;
    }

    this.changeType = function(){
        if(this.autopart) return this;
        if(this.type == 'BIOS'){
            this.type = 'UEFI';
        }else{
            this.type = 'BIOS';
        }
        return this;
    }

    this.setVersion = function(version){
        this.version = version;
        return this;
    }
}

var FreeBSD_Partition = function(s, type){
    this.error = false;
    this.origin = s;
    this.obj = [];
    this.xml = '';
    this.type = type || 'BIOS';
    this.os = 'freebsd';
    this.autopart = false;
    this.parts = [];  //已有分区
    this.auto_xml = ['autopart'];
    this.version = '';

    this.init = function(){
        this.refreshObj(this.origin);
        this.refreshXml();
        return this;
    }

    this.refreshXml = function(_obj){
        var obj = _obj || this.obj
        if(this.autopart){
            this.xml = '';
            this.parts = [];
            this.error = false;
            this.autopart = true;
            return true;
        }
        var xml_arr = [];
        var parts = [];
        for(let i = 0; i<this.obj.length; i++){
            let one = ''
            if(this.obj[i].size){
                one += this.obj[i].size+'M ';
            }else{
                one += 'auto ';
            }
            one += 'freebsd-'+this.obj[i].fs+' ';
            if(this.obj[i].part != 'swap' && this.obj[i].part != 'biosboot'){
                one += this.obj[i].part
            }else{
                one = one.trim()
            }
            parts.push(this.obj[i].part)
            xml_arr.push(one)
        }
        if(xml_arr.length>0){
            xml = '{ '+ xml_arr.join(', ') + ' }'
        }else{
            xml = ''
        }
        this.xml = xml;
        this.parts = parts;
        return true;
    }

    this.refreshObj = function(command){
        if(typeof command != 'undefined'){
            command = command.trim()
        }
        var cmd = command || this.xml;
        if(!cmd){
            this.obj = [];
            this.autopart = false;
            this.error = false;
            this.xml = '';
            this.parts = [];
            return true;
        }
        if(this.checkAuto(cmd)) return true;
        cmd = cmd.replace('{', '').replace('}', '').trim()
        var arr = cmd.split(',')
        var obj = []
        for(let i = 0; i<arr.length; i++){
            let one_arr = arr[i].trim().split(' ')
            if(one_arr.length == 3 || one_arr.length == 2){
                let one = {
                    fs: one_arr[1].replace('freebsd-', ''),
                    part: one_arr[2] 
                }
                if(/\d+/.test(one_arr[0])){
                    if(/G/.test(one_arr[0])){
                        one.size = one_arr[0].replace('G', '')*1024
                    }else if(/M/.test(one_arr[0])){
                        one.size = one_arr[0].replace('M', '')
                    }
                }else{
                    one.size = '';
                }
                if(one.fs == 'swap'){
                    one.part = 'swap'
                }else if(one.fs == 'biosboot'){
                    one.part = 'biosboot'
                }
                obj.push(one)
            }
        } 
        this.obj = obj;
        this.autopart = false;
        this.error = false;
        return true;
    }

    this.add = function(partition){
        let part = partition.part;
        let size = partition.size;

        if(this.parts.indexOf(part) != -1){
            this.error = true;
            return false;
        }
        if(this.obj.length>0 && !size && !this.obj[this.obj.length-1].size){
            this.error = true;
            return false;
        }
        if(part == 'swap'){
            var create_one = {
                fs: "swap",
                part: "swap",
                size: size
            }
        }else if(part == 'biosboot'){
            var create_one = {
                fs: "biosboot",
                part: "biosboot",
                size: size
            }
        }else{
            var create_one = {}
            create_one.fs = "ufs";
            create_one.size = size
            create_one.part = part;
        }
        if(this.obj.length>0 && !this.obj[this.obj.length-1].size){
            this.obj.splice(this.obj.length-1, 0, create_one);
        }else{
            this.obj.push(create_one);
        }
        this.parts.push(create_one.part);
        this.refreshXml();
        this.error = false;
        return true;
    }

    this.modify = function(partition){
        let row = partition.row;
        let part = partition.part;
        let size = partition.size;

        if(this.parts.indexOf(part) != row && this.parts.indexOf(part) != -1){
            this.error = true;
            return false;
        }
        if(size == 0 && row != this.obj.length-1){
            this.error = true;
            return false;
        }
        if(row >= this.obj.length){
            return false;
        }
        if(part == 'swap'){
            this.obj[row].fs = "swap";
            this.obj[row].size = size;
            this.obj[row].part = "swap";
        }else if(part == 'biosboot'){
            this.obj[row].fs = "biosboot";
            this.obj[row].size = size;
            this.obj[row].part = "biosboot";
        }else{
            this.obj[row].size = size;
            this.obj[row].part = part;
        }
        this.refreshXml();
        this.error = false;
        return true;
    }

    this.delete = function(row){
        if(row >= this.obj.length){
            this.error = true;
            return false;
        }
        this.obj.splice(row, 1);
        this.refreshXml();
        this.error = false;
        return true;
    }

    this.checkAuto = function(command){
        var cmd = command || this.xml;
        if(cmd && cmd.trim() == 'autopart'){
            this.autopart = true;
            this.error = false;
            this.parts = [];
            this.xml = '';
            return true;
        }
        return false;
    }

    this.cancelAuto = function(){
        this.autopart = false;
        this.error = false;
    }

    this.setAuto = function(){
        this.xml = '';
        this.obj = [];
        this.autopart = true;
        this.error = false;
        return true;
    }

    this.changeType = function(){
        if(this.autopart) return this;
        if(this.type == 'BIOS'){
            this.type = 'UEFI';
        }else{
            this.type = 'BIOS';
        }
        return this;
    }

    this.setVersion = function(version){
        this.version = version;
        return this
    }
}

