import React = require('react');
import { action, computed, observable, ObservableMap } from 'mobx';
import { observer } from 'mobx-react';
import Select from 'react-select';
import { Doc, DocListCast, Field, StrListCast } from '../../fields/Doc';
import { RichTextField } from '../../fields/RichTextField';
import { StrCast } from '../../fields/Types';
import { DocumentManager } from '../util/DocumentManager';
import { UserOptions } from '../util/GroupManager';
import './FilterPanel.scss';
import { FieldView } from './nodes/FieldView';
import { SearchBox } from './search/SearchBox';
import { undoable } from '../util/UndoManager';
import { AiOutlineMinusSquare, AiOutlinePlusSquare } from 'react-icons/ai';
import { CiCircleRemove } from 'react-icons/ci';
import { Slider, Rail, Handles, Tracks, Ticks } from 'react-compound-slider';
import { TooltipRail, Handle, Tick, Track } from './nodes/SliderBox-components';

//slight bug when you don't click on background canvas before creating filter and the you click on the canvas 

interface filterProps {
    rootDoc: Doc;
}

@observer
export class FilterPanel extends React.Component<filterProps> {
    public static LayoutString(fieldKey: string) {
        return FieldView.LayoutString(FilterPanel, fieldKey);
    }

    /**
     * @returns the relevant doc according to the value of FilterBox._filterScope i.e. either the Current Dashboard or the Current Collection
     */
    @computed get targetDoc() {
        return this.props.rootDoc;
    }
    @computed get targetDocChildKey() {
        const targetView = DocumentManager.Instance.getFirstDocumentView(this.targetDoc);
        return targetView?.ComponentView?.annotationKey ?? targetView?.ComponentView?.fieldKey ?? 'data';
    }
    @computed get targetDocChildren() {
        return [...DocListCast(this.targetDoc?.[this.targetDocChildKey] || Doc.ActiveDashboard?.data), ...DocListCast(this.targetDoc[Doc.LayoutFieldKey(this.targetDoc) + '_sidebar'])];
    }

    @computed get allDocs() {
        const allDocs = new Set<Doc>();
        const targetDoc = this.targetDoc;
        if (targetDoc) {
            SearchBox.foreachRecursiveDoc([this.targetDoc], (depth, doc) => allDocs.add(doc));
        }
        return Array.from(allDocs);
    }

    @computed get _allFacets() {
        // trace();
        const noviceReqFields = ['author', 'tags', 'text', 'type'];
        const noviceLayoutFields: string[] = []; //["_layout_curPage"];
        const noviceFields = [...noviceReqFields, ...noviceLayoutFields];

        const keys = new Set<string>(noviceFields);
        this.allDocs.forEach(doc => SearchBox.documentKeys(doc).filter(key => keys.add(key)));
        const sortedKeys = Array.from(keys.keys())
            .filter(key => key[0])
            .filter(key => key.indexOf('modificationDate') !== -1 || (key[0] === key[0].toUpperCase() && !key.startsWith('_')) || noviceFields.includes(key) || !Doc.noviceMode)
            .sort();
        noviceFields.forEach(key => sortedKeys.splice(sortedKeys.indexOf(key), 1));
        return [...noviceFields, ...sortedKeys];
    }

    @computed get rangeFilters() {
        return StrListCast(this.targetDoc?._childFiltersByRanges).filter((filter, i) => !( i % 3) )
    }

    /**
     * activeFilters( ) -- all filters that currently have a filter set on them in this document (ranges, and others)
     * ["#tags::bob::check", "tags::joe::check", "width", "height"]
     */
    @computed get activeFilters() {
        return StrListCast(this.targetDoc?._childFilters).concat(this.rangeFilters);
    }

    @computed get mapActiveFiltersToFacets() {
        const filters = new Map<string, string>();
        //this.targetDoc.docFilters
        this.activeFilters.map(filter => filters.set(filter.split(Doc.FilterSep)[1] ,filter.split(Doc.FilterSep)[0] ))
        console.log("what is wrong with filters " +filters )
        return filters
    }

    // 
    // activeFacetHeaders() - just the facet names, not the rest of the filter
    //
    // this wants to return all the filter facets that have an existing filter set on them in order to show them in the rendered panel
    // this set may overlap the selectedFilters 
    // if the components reloads, these will still exist and be shown

    // ["#tags", "width", "height"]
    //

    @computed get activeFacetHeaders() {
        const activeHeaders = new Array()

        this.activeFilters.map(filter => activeHeaders.push(filter.split(Doc.FilterSep)[0]) )

    
        return activeHeaders;
    }


    // @computed get currentActiveFilterz(){   // this.existingFilters
    //     const filters = new Map<string, string>();
    //     //this.targetDoc.docFilters
    //     this.activeFilters.map(filter => filters.set(filter.split(Doc.FilterSep)[1] ,filter.split(Doc.FilterSep)[0] ))
    //     console.log("what is wrong with filters " +filters )
    //      return filters
    // }
    
    /**
     * @returns a string array of the current attributes
     */
    // @computed get currentFacets() {
    //     return this.activeFilters.map(filter => filter.split(Doc.FilterSep)[0]);
    // }

    gatherFieldValues(childDocs: Doc[], facetKey: string) {
        const valueSet = new Set<string>();
        let rtFields = 0;
        let subDocs = childDocs;
        if (subDocs.length > 0) {
            let newarray: Doc[] = [];
            while (subDocs.length > 0) {
                newarray = [];
                subDocs.forEach(t => {
                    const facetVal = t[facetKey];
                   // console.log("facetVal " + facetVal)
                    if (facetVal instanceof RichTextField || typeof facetVal === 'string') rtFields++;
                    facetVal !== undefined && valueSet.add(Field.toString(facetVal as Field));
                    (facetVal === true || facetVal == false) && valueSet.add(Field.toString(!facetVal));
                    const fieldKey = Doc.LayoutFieldKey(t);
                    const annos = !Field.toString(Doc.LayoutField(t) as Field).includes('CollectionView');
                    DocListCast(t[annos ? fieldKey + '_annotations' : fieldKey]).forEach(newdoc => newarray.push(newdoc));
                    annos && DocListCast(t[fieldKey + '_sidebar']).forEach(newdoc => newarray.push(newdoc));
                });
                subDocs = newarray;
            }
        }
        //     }
        // });
       
        return { strings: Array.from(valueSet.keys()), rtFields };
    }



    public removeFilter = (filterName: string) => {
        Doc.setDocFilter(this.targetDoc, filterName, undefined, 'remove');
        Doc.setDocRangeFilter(this.targetDoc, filterName, undefined);
    };

    // @observable _chosenFacets = new ObservableMap<string, 'text' | 'checkbox' | 'slider' | 'range'>();
    @observable _chosenFacetsCollapse = new ObservableMap<string, boolean>();
    @observable _collapseReturnKeys = new Array();


    // this computed function gets the active filters and maps them to their headers

    //
    // activeRenderedFacetInfos()
    //    returns renderInfo for all user selected filters and for all existing filters set on the document
    // Map("tags" => {"checkbox"},
   //         "width" => {"rangs", domain:[1978,1992]})
    //

    @computed get activeRenderedFacetInfos(){

        return new Set(
            Array.from(new Set(Array.from(this._selectedFacetHeaders).concat(this.activeFacetHeaders))).map(
                facetHeader => {

                    const facetValues = this.gatherFieldValues(this.targetDocChildren, facetHeader);

                    let nonNumbers = 0;
                    let minVal = Number.MAX_VALUE,
                        maxVal = -Number.MAX_VALUE;
                    facetValues.strings.map(val => {
                        const num = val ? Number(val) : Number.NaN;
                        if (Number.isNaN(num)) {
                            val && nonNumbers++;
                        } else {
                            minVal = Math.min(num, minVal);
                            maxVal = Math.max(num, maxVal);
                        }
                    });


                    if (facetHeader === 'text' ){
                        return {facetHeader: facetHeader, renderType: 'text'}

                    } else if (facetHeader !== 'tags' && nonNumbers / facetValues.strings.length < 0.1){

                        const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1));
                        const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.05)));
                        const ranged = Doc.readDocRangeFilter(this.targetDoc, facetHeader); // not the filter range, but the zooomed in range on the filter
                        return {facetHeader: facetHeader, renderType: 'range', domain: [extendedMinVal, extendedMaxVal], range: ranged ? ranged: [extendedMinVal, extendedMaxVal]}
 
                    } else{
                        return {facetHeader: facetHeader, renderType: 'checkbox'}
                    }
                }
            ))
    }

    // @computed get activeFacets() {  // selectedFacetRenderInfo => []:{facetName, renderInfo}[]   renderInfo: { renderType: text|range|... ,   minVal?: , maxVal?; }

    //     // return new Set(
    //           // Array.from(new Set(Array.from(this.selectedFacetHeaders).concat(this.existingFacetHeaders)))
    //            // .map(facetHeader => { --> getting exisitng filters and new filters that havent been selected but need to remove duplicates 
    //                 // most of what facetClick did before ... 
    //                 // minVal, maxvval...
    //                 //  if (...)
    //                 //      return {key: facet,  renderType: "text"}
    //                 //  else if (... numbers) return {key:facet, renderrType: range, extendedMinVal, maxval
    //                 //      return
    //                 // return ..
    //                 // }))

    //     // console.log("chosen collpase " + this._chosenFacetsCollapse)
    //     const facets = new Map<string, 'text' | 'checkbox' | 'slider' | 'range'>();
    //     this.activeFilters.map(filter => facets.set(filter.split(Doc.FilterSep)[0], filter.split(Doc.FilterSep)[2] === 'match' ? 'text' : 'checkbox'));
    //     // setTimeout(() => StrListCast(this.targetDoc?._childFilters).map(action(filter => this._chosenFacets.set(filter.split(Doc.FilterSep)[0], filter.split(Doc.FilterSep)[2] === 'match' ? 'text' : 'checkbox'))));
    //     return facets;
    // }

    @observable _selectedFacetHeaders = new Set<string>();

    /**
     * user clicks on a filter facet because they want to see it.   
     * this adds this chosen filter to a set of user selected filters called:  selectedFilters
     * if this component reloads, then these filters will go away since they haven't been written to any Doc anywhere
     * 
     *   // this._selectedFacets.add(facetHeader);  .. add to Set() not array
     */
    @action
    facetClick = (facetHeader: string) => { // just when someone chooses a facet

        this._selectedFacetHeaders.add(facetHeader);

         return


        // if (!this.targetDoc) return;
        // const allCollectionDocs = this.targetDocChildren;
        // const facetValues = this.gatherFieldValues(this.targetDocChildren, facetHeader);

        // let nonNumbers = 0;
        // let minVal = Number.MAX_VALUE,
        //     maxVal = -Number.MAX_VALUE;
        // facetValues.strings.map(val => {
        //     const num = val ? Number(val) : Number.NaN;
        //     if (Number.isNaN(num)) {
        //         val && nonNumbers++;
        //     } else {
        //         minVal = Math.min(num, minVal);
        //         maxVal = Math.max(num, maxVal);
        //     }
        // });
        // if (facetHeader === 'text' || (facetValues.rtFields / allCollectionDocs.length > 0.1 && facetValues.strings.length > 20)) {
        //     // this._chosenFacets.set(facetHeader, 'text');
        // } else if (facetHeader !== 'tags' && nonNumbers / facetValues.strings.length < 0.1) {

        //     console.log("in this IF STATEMENE ")

        //     const ranged = Doc.readDocRangeFilter(this.targetDoc, facetHeader); // not the filter range, but the zooomed in range on the filter
        //     const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1));
        //     const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.05)));
        //     // this.targetDoc["year-min"] = 1978
        //     // // facetHeader == year
        //   //  this.targetDoc[newFacetField + '-min'] = ranged === undefined ? extendedMinVal : ranged[0];
        //   //  newFacet[newFacetField + '-max'] = ranged === undefined ? extendedMaxVal : ranged[1];
        //   //  const scriptText = `setDocRangeFilter(this?.target, "${facetHeader}", range)`;
        //     Doc.setDocRangeFilter(this.targetDoc, facetHeader, [extendedMinVal, extendedMaxVal] )


        //     // newFacet = Docs.Create.SliderDocument({
        //     //     title: facetHeader,
        //     //     system: true,
        //     //     target: targetDoc,
        //     //     _fitWidth: true,
        //     //     _height: 40,
        //     //     _stayInCollection: true,
        //     //     treeViewExpandedView: 'layout',
        //     //     _treeViewOpen: true,
        //     //     _forceActive: true,
        //     //     _overflow: 'visible',
        //     // });
        //     // const newFacetField = Doc.LayoutFieldKey(newFacet);
        //     // const ranged = Doc.readDocRangeFilter(targetDoc, facetHeader);
        //     // Doc.GetProto(newFacet).type = DocumentType.COL; // forces item to show an open/close button instead ofa checkbox
        //     // const extendedMinVal = minVal - Math.min(1, Math.floor(Math.abs(maxVal - minVal) * 0.1));
        //     // const extendedMaxVal = Math.max(minVal + 1, maxVal + Math.min(1, Math.ceil(Math.abs(maxVal - minVal) * 0.05)));
        //     // newFacet[newFacetField + '-min'] = ranged === undefined ? extendedMinVal : ranged[0];
        //     // newFacet[newFacetField + '-max'] = ranged === undefined ? extendedMaxVal : ranged[1];
        //     // Doc.GetProto(newFacet)[newFacetField + '-minThumb'] = extendedMinVal;
        //     // Doc.GetProto(newFacet)[newFacetField + '-maxThumb'] = extendedMaxVal;
        //     // const scriptText = `setDocRangeFilter(this?.target, "${facetHeader}", range)`;
        //     // newFacet.onThumbChanged = ScriptField.MakeScript(scriptText, { this: Doc.name, range: 'number' });
        //     // newFacet.data = ComputedField.MakeFunction(`readNumFacetData(self.target, self, "${FilterBox.targetDocChildKey}", "${facetHeader}")`);
       

        // } else {
        //     // this._chosenFacets.set(facetHeader, 'checkbox');
        // }
        // this._chosenFacetsCollapse.set(facetHeader, false)
    };

  
    @action
    sortingCurrentFacetValues = (facetHeader:string) => {

        this._collapseReturnKeys.splice(0)

        for (var key of this.facetValues(facetHeader)){ // all  filters currently set
           // console.log("key : " + key  )
           if (this.mapActiveFiltersToFacets.get(key)){ // work with the current filter selected
            //    console.log("WEREEE HERHEHHHHEHHHHEEE")
                this._collapseReturnKeys.push(key) 
         }
    }

        return (<div className = " filterbox-collpasedAndActive">
          
            {this._collapseReturnKeys.join(',  ') }
            {/* .toString()} */}
        </div>)
        
        
    }
    
    


    facetValues = (facetHeader: string) => {
        const allCollectionDocs = new Set<Doc>();
        SearchBox.foreachRecursiveDoc(this.targetDocChildren, (depth: number, doc: Doc) => allCollectionDocs.add(doc));
        const set = new Set<string>([String.fromCharCode(127) + '--undefined--']);
        if (facetHeader === 'tags')
            allCollectionDocs.forEach(child =>
                StrListCast(child[facetHeader])
                    .filter(h => h)
                    .forEach(key => set.add(key))
            );
        else
            allCollectionDocs.forEach(child => {
                const fieldVal = child[facetHeader] as Field;
                set.add(Field.toString(fieldVal));
                (fieldVal === true || fieldVal === false) && set.add((!fieldVal).toString());
            });
        const facetValues = Array.from(set).filter(v => v);

        let nonNumbers = 0;

        facetValues.map(val => Number.isNaN(Number(val)) && nonNumbers++);
        return nonNumbers / facetValues.length > 0.1 ? facetValues.sort() : facetValues.sort((n1: string, n2: string) => Number(n1) - Number(n2));
    };


    render() {
       // const options = this._allFacets.filter(facet => this.currentFacets.indexOf(facet) === -1).map(facet => ({ value: facet, label: facet }));
        const options = this._allFacets.filter(facet => this.activeFacetHeaders.indexOf(facet) === -1).map(facet => ({ value: facet, label: facet }));

        return (
            <div className="filterBox-treeView">
                <div className="filterBox-select">
                    <div style={{ width: '100%' }}>
                        <Select placeholder="Add a filter..." options={options} isMulti={false} onChange={val => this.facetClick((val as UserOptions).value)} onKeyDown={e => e.stopPropagation()} value={null} closeMenuOnSelect={true} />
                    </div>
                    {/* THE FOLLOWING CODE SHOULD BE DEVELOPER FOR BOOLEAN EXPRESSION (AND / OR) */}
                    {/* <div className="filterBox-select-bool"> 
                        <select className="filterBox-selection" onChange={action(e => this.targetDoc && (this.targetDoc._childFilters_boolean = (e.target as any).value))} defaultValue={StrCast(this.targetDoc?.childFilters_boolean)}>
                            {['AND', 'OR'].map(bool => (
                                <option value={bool} key={bool}>
                                    {bool}
                                </option>
                            ))}
                        </select>
                    </div>{' '} */}
                </div>

                <div className="filterBox-tree" key="tree">
                    {Array.from(this.activeRenderedFacetInfos.keys()).map(renderInfo => ( // iterato over activeFacetRenderInfos  ==> renderInfo  which you can renderInfo.facetHeader
                        <div>
                            <div className  = "filterBox-facetHeader">
                                <div className = "filterBox-facetHeader-Header"> </div>
                                {renderInfo.facetHeader.charAt(0).toUpperCase() + renderInfo.facetHeader.slice(1)}
                                
                                    <div className = "filterBox-facetHeader-collapse"
                                     onClick = {action((e) => { 

                                       
                                        const collapseBoolValue = this._chosenFacetsCollapse.get(renderInfo.facetHeader)
                                        this._chosenFacetsCollapse.set(renderInfo.facetHeader, !collapseBoolValue )})}>
                                        
                                        {this._chosenFacetsCollapse.get(renderInfo.facetHeader) ? <AiOutlinePlusSquare/> :  <AiOutlineMinusSquare/> }    
                                             
                                             </div>
                                
                                    <div className='filterBox-facetHeader-remove'
                                     onClick = {action((e) => { 
                                        for (var key of this.facetValues(renderInfo.facetHeader)){
                                            if (this.mapActiveFiltersToFacets.get(key)){
                                                Doc.setDocFilter(this.targetDoc, renderInfo.facetHeader, key, 'remove')
                                        }}

                                        if (renderInfo.facetHeader)

                                        this._selectedFacetHeaders.delete(renderInfo.facetHeader)

                                        // this.activeFacets.delete(renderInfo.facetHeader)
                                       // this._chosenFacets.delete(facetHeader)
                                        this._chosenFacetsCollapse.delete(renderInfo.facetHeader)

                                        console.log("this is activeFilters " + this.activeFilters)
                                        console.log("this is activeFacetHeaders " + this.activeFacetHeaders)
                                        console.log("thsi is activeRenderedFacetInfos " + this.activeRenderedFacetInfos)
                                        console.log("thsi is selected facet Headers " + this._selectedFacetHeaders )

                                        })}  >
                                            
                                    <CiCircleRemove/> </div> 
                                  {/* </div> */}

                            </div>
                           
                            { this._chosenFacetsCollapse.get(renderInfo.facetHeader) ? 
                            // <div className = 'filterbox-collpasedAndActive' style={{backgroundColor: 'yellow', fontSize: 50}} > {this.sortingCurrentFacetValues(facetHeader)} </div> &&  this._collapseReturnKeys.splice(0)
                            this.sortingCurrentFacetValues(renderInfo.facetHeader) 
                            // &&  this._collapseReturnKeys.splice(0)
                             : this.displayFacetValueFilterUIs(renderInfo.renderType, renderInfo.facetHeader, renderInfo.domain, renderInfo.range ) } 
                            {/*  */}
                        </div>
                    ))}
                </div>
            </div>
        );
    }

    private displayFacetValueFilterUIs(type: string | undefined, facetHeader: string, renderInfoDomain?: number[] | undefined, renderInfoRange?: number[] ): React.ReactNode {
        //  displayFacetValueFilterUIs(renderIinfo)
        switch (type /* renderInfo.type */ ) {
            case 'text': // if (this.chosenFacets.get(facetHeader) === 'text')
                return (
                    <input
                        placeholder={
                            StrListCast(this.targetDoc._childFilters)
                                .find(filter => filter.split(Doc.FilterSep)[0] === facetHeader)
                                ?.split(Doc.FilterSep)[1] ?? '-empty-'
                        }
                        onBlur={undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')}
                        onKeyDown={e => e.key === 'Enter' && undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, e.currentTarget.value, !e.currentTarget.value ? 'remove' : 'match'), 'set text filter')(e)}
                    />
                );
            case 'checkbox':
           
                return this.facetValues(facetHeader).map(fval => {
                  
                    const facetValue = fval;
                    return (
                        <div>
                            <input
                                style={{ width: 20, marginLeft: 20 }}
                                checked={ 
                                        StrListCast(this.targetDoc._childFilters)
                                            .find(filter => filter.split(Doc.FilterSep)[0] === facetHeader && filter.split(Doc.FilterSep)[1] == facetValue)
                                            ?.split(Doc.FilterSep)[2] === 'check'
                                     
                                }
                                type={type}
                                onChange={undoable(e => Doc.setDocFilter(this.targetDoc, facetHeader, fval,  e.target.checked ? 'check' : 'remove'), 'set filter')}
          
                              
                            />
                            {facetValue}
                        </div>
                    );
                })

                case 'range':
                    console.log("in range")

                    const domain = renderInfoDomain;

                    if (domain){
                        return(
                        <Slider mode={2} step={Math.min(1, 0.1 * (domain[1] - domain[0]))} domain={[-1000, 1000]} rootStyle={{ position: 'relative', width: '100%' }} 
                        onChange={ values => Doc.setDocRangeFilter(this.targetDoc, facetHeader, values)} values={renderInfoRange!} >
                            <Rail>{railProps => <TooltipRail {...railProps} />}</Rail>
                            <Handles>
                                {({ handles, activeHandleID, getHandleProps }) => (
                                    <div className="slider-handles">
                                        {handles.map((handle, i) => {
                                            // const value = i === 0 ? defaultValues[0] : defaultValues[1];
                                            return (
                                                <div >
                                                    <Handle key={handle.id} handle={handle} domain={domain} isActive={handle.id === activeHandleID} getHandleProps={getHandleProps} />
                                                </div>
                                            );
                                        })}
                                    </div>
                                )}
                            </Handles>
                            <Tracks left={false} right={false}>
                                {({ tracks, getTrackProps }) => (
                                    <div className="slider-tracks">
                                        {tracks.map(({ id, source, target }) => (
                                            <Track key={id} source={source} target={target} disabled={false} getTrackProps={getTrackProps} />
                                        ))}
                                    </div>
                                )}
                            </Tracks>
                            <Ticks count={5}>
                                {({ ticks }) => (
                                    <div className="slider-ticks">
                                        {ticks.map(tick => (
                                            <Tick key={tick.id} tick={tick} count={ticks.length} format={(val: number) => val.toString()} />
                                        ))}
                                    </div>
                                )}
                            </Ticks>
                    </Slider>
                    )
                    }

                    

                // case 'range'
                // return <Slider ...
                //  return <slider domain={renderInfo.domain}> domain is number[] for min and max
                //         onChange = { ...     Doc.setDocRangeFilter(this.targetDoc, facetHeader, [extendedMinVal, extendedMaxVal] ) }
                //
                //  OR

                // return <div>
                //     <slider domain={renderInfo.domain}> // domain is number[] for min and max
                //     <dimain changing handles >
                //  <?div
                
                ;
        }
    }
}


