import React, { Component } from 'react';

import Dialog, {
  DialogActions,
  DialogContent,
  DialogContentText,
  DialogTitle,
} from 'material-ui/Dialog';
import _ from 'lodash'
import Drawer from 'material-ui/Drawer';

import './TPICodeMirror.css'
import TPICodeSetting from './TPICodeSetting'


import { fromStore, toStore } from 'educoder'
require('codemirror/lib/codemirror.css');
// require('codemirror/mode/javascript/javascript');
// require('codemirror/mode/xml/xml');
// require('codemirror/mode/markdown/markdown');

function getModeByMirrorName(mirror_name) {
    let mode = 'javascript'
    if (mirror_name && mirror_name.length) {
        for (let i = 0; i < mirror_name.length; i++) {
            let modeVal = mirrorNameModeMap[mirror_name[i]];
            if (modeVal) {
                mode = modeVal;
                break;
            }
        }
    }
    return mode;
}
const _extraKeys = {"Alt-/": "autocomplete"};
function createCMOptions(mirror_name) {
    let mode = getModeByMirrorName(mirror_name)

    let cmOptions = {
        lineNumbers: true,
        mode: mode,
        theme: "railscasts",
        indentUnit:4,
        matchBrackets: true,
        autoRefresh: true,
        smartIndent: true,//智能换行
        extraKeys: _extraKeys,
        autofocus: true,
        styleActiveLine: true,
        lint: true, 
        gutters: ["CodeMirror-linenumbers", "breakpoints", "CodeMirror-lint-markers"]
    };
    return cmOptions;
}

const mirrorNameModeMap = {
    'JFinal': 'text/x-java',
    'Java': 'text/x-java',
    'Kotlin': 'text/x-kotlin',
    'C/C++' : 'text/x-c++src',
    'MachineLearning': {
                name: "python",
                version: 3,
                singleLineStringErrors: false
    },
    'Python2.7': {
                name: "python",
                version: 3,
                singleLineStringErrors: false
    },
    'Python3.6': {
                name: "python",
                version: 3,
                singleLineStringErrors: false
    },
}


let extend_editor = null;
let notCallCodeMirrorOnChangeFlag = false;

const $ = window.$;

/*
    lint的实现，目前只支持javascript\html\css\coffeescript\json的lint，支持的语言版本有待考量
    底层gutter mark实现使用的还是setGutterMarker接口（参见lint.js的189行）

    红色波浪线实现方式：
    _cm.markText({line:4, ch:32}, {line:4, ch:40}, {
        className: "CodeMirror-lint-mark-error",
        __annotation: {message: "Expected an identifier and instead saw ';'.", severity: "error"}
    })
*/
class TPICodeMirror extends Component {

	constructor(props) {
		super(props)
		this.state = {
            cmFontSize: fromStore('cmFontSize', 16),
            autoCompleteSwitch: fromStore('autoCompleteSwitch', true),
		}
	}
    onAutoCompleteSwitchChange = () => {
        extend_editor.setOption({
            extraKeys: this.state.autoCompleteSwitch ? _extraKeys : {"Ctrl-Alt-/": "autocomplete"}
        })
        toStore('autoCompleteSwitch', !this.state.autoCompleteSwitch)
        this.setState({ autoCompleteSwitch: !this.state.autoCompleteSwitch })
    }
	componentDidUpdate(prevProps, prevState, snapshot) {
        const { game, mirror_name } = this.props
        if (extend_editor && !_.isEqual(prevProps.mirror_name, mirror_name)) {
            extend_editor.setOption("mode", getModeByMirrorName(mirror_name));
        } 
    }
	componentDidMount() {
        let cmOptions = createCMOptions(this.props.mirror_name)
        extend_editor = window.CodeMirror.fromTextArea(window.$('#extend-challenge-file-edit')[0]
        	, cmOptions);

        extend_editor.on('beforeChange', (cm,change) => {
            // if ( ~readOnlyLines.indexOf(change.from.line) ) {
            //     change.cancel();
            // }
            if (change.origin === "setValue") {
                return;
            }
            if (!this.props.isEditablePath) {
                change.cancel();
            }
        });

        extend_editor.on('change', (cMirror) => {
            // get value right from instance
            // $('#extend-challenge-file-edit').val(cMirror.getValue());
            if (notCallCodeMirrorOnChangeFlag === true) {
                // 避免死循环 onRepositoryCodeUpdate 和 componentWillReceiveProps
                notCallCodeMirrorOnChangeFlag = false;
                return;
            }
            
            this.props.onRepositoryCodeUpdate(cMirror.getValue())
        });

        extend_editor.refresh();
		// wtf 加了这句后，禁用快捷键唤起autocomplete就生效了。。。
        extend_editor.setOption('extraKeys', {"Ctrl-Alt-/": "autocomplete"})

        // 拖拽也需要用 ： window.editor_CodeMirror.refresh()
        window.editor_CodeMirror = extend_editor;	// tpi_html_show需要用到

        this.initHint()
        this.props.codemirrorDidMount();
    }
    initHint() {
    	window.CodeMirror.showHint && extend_editor.on('keyup', (editor_arg, event) => {
		    if (event.key === 'ArrowDown' || event.key === 'ArrowUp' || event.key === 'ArrowLeft' 
		      || event.key === 'ArrowRight' || event.key === 'Enter' || event.key === 'Space'
		      // 32 空格
		      || event.key === 'Escape' || event.keyCode === 32 ) { // 避免使用键盘选择hint的时候再次触发

                // TODO 增加的键 ：ctrl+v ctrl+z
		      return;
		    }
            var cursor = extend_editor.getCursor();
            var lineText = extend_editor.getLine(cursor.line);
            const lastCharInput = lineText.charAt(cursor.ch - 1).trim()
            if (lineText && /^[a-zA-Z0-9_]+$/.test(lastCharInput) === true) {
                this.state.autoCompleteSwitch && extend_editor.showHint(editor_arg);
            }
		});

        let languageHints;
        if (this.props.challenge.isHtml === true) {
            languageHints = allCssPropValueArray
        }
		window.CodeMirror.on(extend_editor, "hinting", (words) => {
		  // extend_editor.state.needToClearJSHint = true;  // 每次调用完成后，needToClearJSHint会被置为false
		  var result = window.CodeMirror.hint.anyword(extend_editor)  // 获取当前editor里的单词
		  

		  extend_editor.state.myhints = languageHints || []
		  var myhints = extend_editor.state.myhints;
		  result.list.forEach(function(item) {
		    if (myhints.indexOf(item) === -1) myhints.push(item)
		  })
		})

		window.document.onkeydown = (e) => { 
         	e=window.event||e;
         	if(e.keyCode== 83 && e.ctrlKey){ 
        	   /*延迟，兼容FF浏览器  */
        	    // setTimeout(function(){
        		  	// alert('ctrl+s'); 
        	   	// },1); 
				this.props.doFileUpdateRequestOnCodeMirrorBlur();
                return false;      
   		    }    
       };
		 
		window.CodeMirror.registerHelper(	
			"hintWords", "javascript", 
			(
            // string
			"charAt charCodeAt indexOf lastIndexOf substring substr slice trim trimLeft trimRight " +
	        "toUpperCase toLowerCase split concat match replace search " +
            // array
	        "length concat join splice push pop shift unshift slice reverse sort indexOf " +
	        "lastIndexOf every some filter forEach map reduce reduceRight " +
            // Math
            "sin cos tan abs ceil floor exp max min pow PI random " +

            "console log " +
	        "prototype apply call bind " +
			"double float int long short null true false enum super this void auto for register static const friend mutable explicit virtual template typename " +
	        "break continue return do while if else for instanceof switch case default try catch finally throw throws assert import byte char delete " +
	        "export operator with " +
	        "print exec raise lambda private protected public abstract class extends final implements interface native new static " +
	        "String vector Boolean function").split(" "));
    }

    componentWillReceiveProps(newProps) {

        if (this.props.codeLoading === true && newProps.codeLoading === false 
                && newProps.repositoryCode != extend_editor.getValue()) {
            // newProps.repositoryCode !== this.props.repositoryCode && 
            notCallCodeMirrorOnChangeFlag = true;
            
            
            // 重要：setState(因获取代码、重置代码等接口引起的调用)调用引起的变化才需要setValue
            extend_editor.setValue(newProps.repositoryCode)

            // Clears the editor's undo history.
            extend_editor.clearHistory()
        }
    }

    onFontSizeChange = (value) => {
        toStore('cmFontSize', value),

        this.setState({ cmFontSize: value });
    } 

	render() {
		const { repositoryCode, showSettingDrawer, settingDrawerOpen } = this.props;
		const { cmFontSize } = this.state;
         
	    return (
            <React.Fragment>
                <Drawer
                    anchor="right"

                    className="settingDrawer"

                    width={260}
                    open={settingDrawerOpen}
                    
                    onClose={() => showSettingDrawer( false )}
                >
                    <TPICodeSetting {...this.props} {...this.state} 
                        onFontSizeChange={this.onFontSizeChange}
                        onAutoCompleteSwitchChange={this.onAutoCompleteSwitchChange}
                         ></TPICodeSetting>
                </Drawer>
                <div className="cmWrapper" style={{fontSize: `${cmFontSize}px`}}>
    	    	  <textarea className = "" id="extend-challenge-file-edit" name="content">{repositoryCode}</textarea>
                </div>
            </React.Fragment>
	    );
  	}
}


export default ( TPICodeMirror ) ;

// prop     http://css-infos.net/
const allCssPropertiesArray = 
`alignment-adjust
alignment-baseline
appearance
azimuth
background
background-attachment
background-clip
background-color
background-image
background-origin
background-position
background-repeat
background-size
baseline-shift
bookmark-label
bookmark-level
bookmark-target
border
border-bottom
border-bottom-color
border-bottom-left-radius
border-bottom-right-radius
border-bottom-style
border-bottom-width
border-clip
border-collapse
border-color
border-image
border-left
border-left-color
border-left-style
border-left-width
border-radius
border-right
border-right-color
border-right-style
border-right-width
border-spacing
border-style
border-top
border-top-color
border-top-left-radius
border-top-right-radius
border-top-style
border-top-width
border-width
bottom
box-decoration-break
box-shadow
caption-side
clear
clip
color
column-count
column-fill
column-gap
column-rule
column-rule-color
column-rule-style
column-rule-width
column-span
column-width
columns
content
counter-increment
counter-reset
crop
cue
cue-after
cue-before
cursor
direction
display
dominant-baseline
drop-initial-after-adjust
drop-initial-after-align
drop-initial-before-adjust
drop-initial-before-align
drop-initial-size
drop-initial-value
elevation
empty-cells
fit
fit-position
float
float-offset
font
font-family
font-size
font-size-adjust
font-stretch
font-style
font-variant
font-weight
grid-columns
grid-rows
hanging-punctuation
height
hyphenate-after
hyphenate-before
hyphenate-character
hyphenate-lines
hyphenate-resource
hyphens
icon
image-orientation
image-resolution
inline-box-align
left
letter-spacing
line-height
line-stacking
line-stacking-ruby
line-stacking-shift
line-stacking-strategy
list-style
list-style-image
list-style-position
list-style-type
margin
margin-bottom
margin-left
margin-right
margin-top
mark
mark-after
mark-before
marker-offset
marks
marquee-direction
marquee-loop
marquee-speed
marquee-style
max-height
max-width
min-height
min-width
move-to
nav-down
nav-index
nav-left
nav-right
nav-up
opacity
orphans
outline
outline-color
outline-offset
outline-style
outline-width
overflow
overflow-style
overflow-x
overflow-y
padding
padding-bottom
padding-left
padding-right
padding-top
page
page-break-after
page-break-before
page-break-inside
page-policy
pause
pause-after
pause-before
phonemes
pitch
pitch-range
play-during
pointer-events
position
presentation-level
punctuation-trim
quotes
rendering-intent
resize
rest
rest-after
rest-before
richness
right
rotation
rotation-point
ruby-align
ruby-overhang
ruby-position
ruby-span
size
speak
speak-header
speak-numeral
speak-punctuation
speech-rate
stress
string-set
tab-side
table-layout
target
target-name
target-new
target-position
text-align
text-align-last
text-decoration
text-emphasis
text-height
text-indent
text-justify
text-outline
text-overflow
text-shadow
text-transform
text-wrap
top
unicode-bidi
vertical-align
visibility
voice-balance
voice-duration
voice-family
voice-pitch
voice-pitch-range
voice-rate
voice-stress
voice-volume
volume
white-space
white-space-collapse
widows
width
word-break
word-spacing
word-wrap
z-index`.split('\n')

// value    http://www.siliconbaytraining.com/pages/csspv.html
/*  var array =[]; $('.test tr td:nth-child(2) font').each((index, item) => array = array.concat($(item).text().split(', ')))
    var mySet = new Set(array)
    array = Array.from(mySet)
    var array2=array.filter(item=> {return item.indexOf('(') == -1 && item.length < 18 && item != 'Values'})
    array2 = array2.map((item) => item.trim().replace('↵',''))
*/
const allCssValueArray = "none,inherit,normal,wider,narrower,ultra-condensed,semi-condensed,semi-expanded,expanded,extra-expanded,ultra-expanded,italic,oblique,small-caps,bold,bolder,lighter,xx-small,x-small,small,medium,large,x-large,xx-large,larger,smaller,1em,left,right,center,justify,underline,overline,line-through,blink,capitalize,uppercase,lowercase,scroll,fixed,transparent,top,bottom,repeat,repeat-x,repeat-y,no-repeat,auto,thin,thick,dotted,dashed,solid,double,groove,ridge,inset,outset,disc,circle,square,decimal,lower-roman,upper-roman,lower-alpha,upper-alpha,upper-latin,hebrew,armenian,georgian,cjk-ideographic,hiragana,katakana,hiragana-iroha,katakana-iroh,outside,inside,pre,nowrap,crosshair,default,pointer,move,e-resize,ne-resize,nw-resize,n-resize,se-resize,sw-resize,s-resize,w-resize,text,wait,help,invert,visible,hidden,open-quote,close-quote,no-open-quote,no-close-quote,inherit,none,both,ltr,rtl,inline,block,list-item,run-in,compact,marker,table,inline-table,table-row-group,table-row,table-caption,static,absolute,relative,embed,bidi-override,baseline,sub,super,text-top,middle,text-bottom,collapse,separate,show,hide,once,always,number,percentage,silent,x-soft,soft,loud,x-loud,spell-out,time,percentage,uri,mix,angle,left-side,far-left,center-left,center-right,far-right,right-side,behind,leftwards,rightwards,below,level,above,higher,lower,x-slow,slow,fast,x-fast,faster,slower,frequency,x-low,low,high,x-high,code,digits,continuous".split(',')
const allCssPropValueArray = allCssPropertiesArray.concat(allCssValueArray)