<!DOCTYPE html>
<html>

<head>
    <meta charset='utf-8' />
    <title>TiTiler - STAC Viewer</title>
    <meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />

    <script src='https://api.tiles.mapbox.com/mapbox-gl-js/v1.6.1/mapbox-gl.js'></script>
    <link href='https://api.tiles.mapbox.com/mapbox-gl-js/v1.6.1/mapbox-gl.css' rel='stylesheet' />

    <link href='https://api.mapbox.com/mapbox-assembly/v0.23.2/assembly.min.css' rel='stylesheet'>
    <script src='https://api.mapbox.com/mapbox-assembly/v0.23.2/assembly.js'></script>

    <style>
        body {
            margin: 0;
            padding: 0;
            width: 100%;
            height: 100%;
        }

        #map {
            position: absolute;
            top: 0;
            bottom: 0;
            width: 100%;
        }

        .zoom-info {
            z-index: 10;
            position: absolute;
            bottom: 17px;
            right: 0;
            padding: 5px;
            width: auto;
            height: auto;
            font-size: 12px;
            color: #000;
        }

        .loading-map {
            position: absolute;
            width: 100%;
            height: 100%;
            color: #FFF;
            background-color: #000;
            text-align: center;
            opacity: 0.5;
            font-size: 45px;
        }

        .loading-map.off {
            opacity: 0;
            -o-transition: all .5s ease;
            -webkit-transition: all .5s ease;
            -moz-transition: all .5s ease;
            -ms-transition: all .5s ease;
            transition: all ease .5s;
            visibility: hidden;
        }

        .middle-center {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
        }

        .middle-center * {
            display: block;
            padding: 5px;
        }

        #menu {
            left: 0;
            top: 0;
            -o-transition: all .5s ease;
            -webkit-transition: all .5s ease;
            -moz-transition: all .5s ease;
            -ms-transition: all .5s ease;
            transition: all ease .5s;
        }

        #menu.off {
            left: -360px;
            -o-transition: all .5s ease;
            -webkit-transition: all .5s ease;
            -moz-transition: all .5s ease;
            -ms-transition: all .5s ease;
            transition: all ease .5s;
        }

        #toolbar {
            height: 20px;
        }

        #toolbar li {
            display: block;
            color: #000;
            background-color: #fff;
            font-weight: 700;
            font-size: 12px;
            padding: 3px;
            height: 100%;
            width: 100%;
            text-transform: uppercase;
            text-align: center;
            text-decoration: none;
            outline: 0;
            cursor: pointer;
            -webkit-touch-callout: none;
            -webkit-user-select: none;
            -moz-user-select: none;
            -ms-user-select: none;
            user-select: none;
        }

        #toolbar li svg {
            font-size: 16px;
            line-height: 16px;
        }

        #toolbar li:hover {
            background-color: #ddd;
        }

        #toolbar li.active {
            color: #fff;
            background-color: #000;
        }

        #toolbar li.disabled {
            pointer-events: none;
            opacity: 0.4;
        }

        #menu-content section {
            display: none;
        }

        #menu-content section.active {
            display: inherit;
        }

        #hide-arrow {
            -o-transition: all .5s ease;
            -webkit-transition: all .5s ease;
            -moz-transition: all .5s ease;
            -ms-transition: all .5s ease;
            transition: all ease .5s;
        }

        #hide-arrow.off {
            transform: rotate(-180deg);
        }

        input:checked+.toggle {
            background: #fff;
            border: #000;
        }

        #btn-hide {
            position: absolute;
            top: 0;
            height: 35px;
            font-size: 35px;
            line-height: 35px;
            vertical-align: middle;
            right: -35px;
            color: #28333b;
            background-color: #fff;
        }

        #btn-hide:hover {
            color: #fff;
            background-color: #28333b;
            cursor: pointer;
        }

        .line-red {
            fill: none;
            stroke: red;
            stroke-width: 1.5px;
        }

        .line-green {
            fill: none;
            stroke: green;
            stroke-width: 1.5px;
        }

        .line-blue {
            fill: none;
            stroke: blue;
            stroke-width: 1.5px;
        }

        @media(max-width: 767px) {

            #menu.off {
                left: -240px;
                -o-transition: all .5s ease;
                -webkit-transition: all .5s ease;
                -moz-transition: all .5s ease;
                -ms-transition: all .5s ease;
                transition: all ease .5s;
            }

            .mapboxgl-ctrl-attrib {
                font-size: 10px;
            }
        }
    </style>
</head>

<body>
    <div id='selector' class='fixed top right bottom left scroll-auto bg-darken25 z3'>
        <div class='bg-white middle-center w600 px12 py12 round'>
            <div class='txt-h5 mt6 mb6 color-black'>Enter STAC Item url</div>
            <input id="stac" class='input wmax-full inline-block' value="" placeholder='STAC Item url here' />
            <button id="launch" class='btn bts--xs btn--stroke bg-darken25-on-hover inline-block mt12 '>Apply</button>
        </div>
    </div>

    <div id='menu' class='flex-child w240 w360-ml absolute bg-gray-faint z2 off'>
        <div id='asset-section' class='px6 py6 wmax-full'> 
            <div class='txt-h5 mb6 color-black'><svg class='icon icon--l inline-block'><use xlink:href='#icon-layers' /></svg>Assets</div>
            <div class='select-container'>
                <select id='asset-selector' class='select select--s select--stroke pl6 wmax-full'></select>
                <div class='select-arrow color-black'></div>
            </div>

            <div class='pt6'>Allow assets composition</div>
            <label class='switch-container'>
                <input id='compose-switch' type='checkbox' />
                <div class='switch'></div>
            </label>
        </div>

        <ul id='toolbar' class='grid'>
            <li id='3b' class="col col--6 active" title="rgb" onclick="switchPane(this)">
                <svg class='icon icon--l inline-block'>
                    <use xlink:href='#icon-menu' /></svg>
            </li>
            <li id='1b' class="col col--6" title="band" onclick="switchPane(this)">
                <svg class='icon icon--l inline-block'>
                    <use xlink:href='#icon-minus' /></svg>
            </li>
        </ul>

        <div id='menu-content' class='relative'>

            <!-- RGB Selection -->
            <section id='3b-section' class='px12 pt12 pb6 active'>
                <div class='txt-h5 mb6 color-black'><svg class='icon icon--l inline-block'><use xlink:href='#icon-layers' /></svg> RGB</div>
                <div id='rgb-buttons' class='align-center px6 py6'>
                    <div class='select-container'>
                        <select id='r-selector' class='select select--s select--stroke wmax-full color-red'></select>
                        <div class='select-arrow color-black'></div>
                    </div>

                    <div class='select-container'>
                        <select id='g-selector' class='select select--s select--stroke wmax-full color-green'></select>
                        <div class='select-arrow color-black'></div>
                    </div>

                    <div class='select-container'>
                        <select id='b-selector' class='select select--s select--stroke wmax-full color-blue'></select>
                        <div class='select-arrow color-black'></div>
                    </div>
                </div>
            </section>

            <!-- 1 Band Selection -->
            <section id='1b-section' class='px12 pt12 pb6'>
                <div class='txt-h5 mb6 color-black'>
                    <svg class='icon icon--l inline-block'><use xlink:href='#icon-layers' /></svg> Layers
                </div>
                <div class='select-container wmax-full'>
                    <select id='layer-selector' class='select select--s select--stroke wmax-full color-black'></select>
                    <div class='select-arrow color-black'></div>
                </div>

                <!-- Color Map -->
                <div id='colormap-section'>
                    <div class='txt-h5 mt6 mb6 color-black'><svg class='icon icon--l inline-block'>
                            <use xlink:href='#icon-palette' /></svg> Color Map</div>
                    <div class='select-container wmax-full'>
                        <select id='colormap-selector' class='select select--s select--stroke wmax-full color-black'>
                            <option value='b&w'>Internal</option>
                            <option value=cfastie>CFastie</option>
                            <option value=rplumbo>RPlumbo</option>
                            <option value=schwarzwald>Schwarzwald (elevation)</option>
                            <option value=viridis>Viridis</option>
                            <option value=rdbu_r>Blue-Red</option>
                            <option value=bugn>Blue-Green</option>
                            <option value=ylgn>Yellow-Green</option>
                            <option value=magma>Magma</option>
                            <option value=gist_earth>Earth</option>
                            <option value=ocean>Ocean</option>
                            <option value=terrain>Terrain</option>
                        </select>
                        <div class='select-arrow color-black'></div>
                    </div>
                </div>
            </section>

            <div id='rescale-section' class='px6 py6'>
                <div class='txt-h5 mb6 color-black'>Histogram min/max</div>
                <div>
                    <input id="min-value" class='input input--s wmax60 inline-block align-center color-black' value='0' />
                    <input id="max-value" class='input input--s wmax60 inline-block align-center color-black' value='1000' />
                </div>
            </div>
            
            <div id='cformula-section' class='px6 py6'>
                <div class='txt-h5 mb6 color-black'>Color Formula</div>
                <input id="ColorFormulaValue" class='input input--s w-full color-black' value='' />
            </div>
            <button id="updateColor" class='btn bts--xs btn--stroke bg-darken25-on-hover txt-s color-black ml12'>Apply</button>

            <!-- Resampling -->
            <div class='px12 pt12 pb6'>
                <div class='txt-h5 mt6 mb6 color-black'>Resampling Method</div>
                <div id='resamp-selector' class='toggle-group bg-gray-faint mt6 mb6' style="line-height: 0">
                    <label class='toggle-container'>
                        <input value='bilinear' name='toggle-resamp' type='radio' />
                        <div title='bilinear' class='toggle color-black color-gray-dark-on-hover'>bilinear</div>
                    </label>
                    <label class='toggle-container'>
                        <input value='cubic' name='toggle-resamp' type='radio' />
                        <div title='cubic' class='toggle color-black color-gray-dark-on-hover'>cubic</div>
                    </label>
                    <label class='toggle-container'>
                        <input value='nearest' checked='checked' name='toggle-resamp' type='radio' />
                        <div title='nearest' class='toggle color-black color-gray-dark-on-hover'>nearest</div>
                    </label>
                </div>
            </div>
        </div>
        <button id='btn-hide'><svg id='hide-arrow' class='icon'>
                <use xlink:href='#icon-arrow-right' /></svg></button>
    </div>

    <div id='map'>
        <div id='loader' class="loading-map z3">
            <div class="middle-center">
                <div class="round animation-spin animation--infinite animation--speed-1">
                    <svg class='icon icon--l inline-block'>
                        <use xlink:href='#icon-satellite' /></svg>
                </div>
            </div>
        </div>
        <div class="zoom-info"><span id="zoom"></span></div>
    </div>

    <script>
        var scope = { separate: false, assets: [], metadata: {} }

        const tilejson_endpoint = "{{ tilejson }}"
        const metadata_endpoint = "{{ metadata }}"

        var map = new mapboxgl.Map({
            container: 'map',
            style: { version: 8, sources: {}, layers: [] },
            center: [0, 0],
            zoom: 1
        })

        const parseParams = (w_loc) => {
            const param_list = w_loc.replace('?', '').split('&')
            const out_params = {}
            for (let i = 0; i < param_list.length; i++) {
                let tPar = param_list[i].split('=')
                out_params[tPar[0]] = tPar[1]
            }
            return out_params
        }

        map.on('zoom', function (e) {
            const z = (map.getZoom()).toString().slice(0, 6)
            document.getElementById('zoom').textContent = z
        })

        const set1bViz = () => {
            const asset = document.getElementById('asset-selector').value

            params = {
                url: scope.url,
                assets: asset,
                resampling_method: document.getElementById('resamp-selector').querySelector("input[name='toggle-resamp']:checked").value
            }

            const active_layer = document.getElementById('layer-selector')[document.getElementById('layer-selector').selectedIndex]
            indexes = active_layer.getAttribute('data-indexes')
            params.bidx = indexes

            if (['uint8', 'int8', 'byte'].indexOf(scope.metadata[params.assets].dtype) === -1) {
                const minV = parseFloat(document.getElementById('min-value').value)
                const maxV = parseFloat(document.getElementById('max-value').value)
                params.rescale = `${minV},${maxV}`
            }
 
            if (document.getElementById('ColorFormulaValue').value !== '') {
                params.color_formula = document.getElementById('ColorFormulaValue').value
            }

            const cmap = document.getElementById('colormap-selector')[document.getElementById('colormap-selector').selectedIndex]
            if (cmap.value !== 'b&w') params.color_map = cmap.value

            const url_params = Object.keys(params).map(i => `${i}=${params[i]}`).join('&')
            let url = `${tilejson_endpoint}?${url_params}`
            map.addSource('raster', { type: 'raster', url: url, tileSize: 512 })
            map.addLayer({  id: 'raster', type: 'raster', source: 'raster' })
            document.getElementById('loader').classList.add('off')
        }

        const set3bViz = () => {
            const r = document.getElementById('r-selector').value
            const g = document.getElementById('g-selector').value
            const b = document.getElementById('b-selector').value

            params = {
                url: scope.url,
                resampling_method: document.getElementById('resamp-selector').querySelector("input[name='toggle-resamp']:checked").value
            }

            if (scope.separate === true) {
                params.assets = `${r},${g},${b}`
                params.bidx = 1
                const minV = parseFloat(document.getElementById('min-value').value)
                const maxV = parseFloat(document.getElementById('max-value').value)
                params.rescale = `${minV},${maxV}`

            } else {
                params.assets = document.getElementById('asset-selector').value
                params.bidx = `${r},${g},${b}`

                if (['uint8', 'int8', 'byte'].indexOf(scope.metadata[params.assets].dtype) === -1) {
                    const minV = parseFloat(document.getElementById('min-value').value)
                    const maxV = parseFloat(document.getElementById('max-value').value)
                    params.rescale = `${minV},${maxV}`
                }
            }

            if (document.getElementById('ColorFormulaValue').value !== '') {
                params.color_formula = document.getElementById('ColorFormulaValue').value
            }

            const url_params = Object.keys(params).map(i => `${i}=${params[i]}`).join('&')
            let url = `${tilejson_endpoint}?${url_params}`
            map.addSource('raster', { type: 'raster', url: url, tileSize: 512 })
            map.addLayer({  id: 'raster', type: 'raster', source: 'raster' })
            document.getElementById('loader').classList.add('off')
        }

        const switchViz = () => {
            if (map.getLayer('raster')) map.removeLayer('raster')
            if (map.getSource('raster')) map.removeSource('raster')

            const rasterType = document.getElementById('toolbar').querySelector(".active").id
            switch (rasterType) {
                case '1b':
                    set1bViz()
                    break
                case '3b':
                    set3bViz()
                    break
                default:
                    throw new Error(`Invalid ${rasterType}`)
            }
        }

       
        document.getElementById('btn-hide').addEventListener('click', () => {
            document.getElementById('hide-arrow').classList.toggle('off')
            document.getElementById('menu').classList.toggle('off')
        })

        document.getElementById('resamp-selector').addEventListener('change', (e) => {
            switchViz()
        })

        document.getElementById('layer-selector').addEventListener('change', () => {
            switchViz()
        })

        document.getElementById('asset-selector').addEventListener('change', () => {
            const asset = document.getElementById('asset-selector').value
            updateUI()
            switchViz(asset)
        })

        document.getElementById('compose-switch').addEventListener('change', () => {
            updateUI()
        })

        document.getElementById('r-selector').addEventListener('change', () => { switchViz() })
        document.getElementById('g-selector').addEventListener('change', () => { switchViz() })
        document.getElementById('b-selector').addEventListener('change', () => { switchViz() })

        document.getElementById('colormap-selector').addEventListener('change', () => {
            switchViz()
        })
        
        document.getElementById('updateColor').addEventListener('click', () => {
            switchViz()
        })

        const switchPane = (event) => {
            const cur = document.getElementById('toolbar').querySelector(".active")
            const activeViz = cur.id
            const nextViz = event.id
            cur.classList.toggle('active')
            event.classList.toggle('active')

            const curSection = document.getElementById(`${activeViz}-section`)
            curSection.classList.toggle('active')
            const nextSection = document.getElementById(`${nextViz}-section`)
            nextSection.classList.toggle('active')
            switchViz()
        }

        const bboxPolygon = (bounds) => {
            return {
                'type': 'Feature',
                'geometry': {
                    'type': 'Polygon',
                    'coordinates': [[
                        [bounds[0], bounds[1]],
                        [bounds[2], bounds[1]],
                        [bounds[2], bounds[3]],
                        [bounds[0], bounds[3]],
                        [bounds[0], bounds[1]]
                    ]]
                },
                'properties': {}
            }
        }

        const addAOI = (bounds) => {
            if (map.getLayer('aoi-polygon')) map.removeLayer('aoi-polygon')
            if (map.getSource('aoi')) map.removeSource('aoi')


            const geojson = {
                "type": "FeatureCollection",
                "features": [bboxPolygon(bounds)]
            }

            map.addSource('aoi', {
                'type': 'geojson',
                'data': geojson
            })

            map.addLayer({
                id: 'aoi-polygon',
                type: 'line',
                source: 'aoi',
                layout: {
                    'line-cap': 'round',
                    'line-join': 'round'
                },
                paint: {
                    'line-color': '#3bb2d0',
                    'line-width': 1
                }
            })
            return
        }

        const updateUI = () => {
            const is_checked = document.getElementById("compose-switch").checked
            scope.separate = is_checked

            document.getElementById('loader').classList.remove('off')

            const rList = document.getElementById('r-selector')
            rList.innerHTML = ''
            const bList = document.getElementById('b-selector')
            bList.innerHTML = ''
            const gList = document.getElementById('g-selector')
            gList.innerHTML = ''

            if (scope.separate === true) {
                document.getElementById('asset-selector').classList.add('disabled')
                document.getElementById("asset-selector").disabled = true

                document.getElementById('3b').classList.add('active')
                document.getElementById('3b').classList.remove('disabled')
                document.getElementById('3b-section').classList.add('active')

                document.getElementById('1b').classList.remove('active')
                document.getElementById('1b').classList.add('disabled')
                document.getElementById('1b-section').classList.remove('active')                
                
                const nbands = scope.assets.length

                for (var i = 0; i < nbands; i++) {
                    let l = document.createElement('option')
                    l.value = scope.assets[i]
                    l.setAttribute('data-indexes', i.toString())
                    l.text = scope.assets[i]
                    if (i === 0) l.selected = "selected"
                    rList.appendChild(l)
                }

                for (var i = 0; i < nbands; i++) {
                    let l = document.createElement('option')
                    l.value = scope.assets[i]
                    l.setAttribute('data-indexes', i.toString())
                    l.text = scope.assets[i]
                    if (i === 1) l.selected = "selected"
                    gList.appendChild(l)
                }

                for (var i = 0; i < nbands; i++) {
                    let l = document.createElement('option')
                    l.value = scope.assets[i]
                    l.setAttribute('data-indexes', i.toString())
                    l.text = scope.assets[i]
                    l.selected = "selected"
                    bList.appendChild(l)
                }

                document.getElementById('rescale-section').classList.remove('none')

            } else {
                document.getElementById('1b').classList.remove('disabled')
                document.getElementById("asset-selector").disabled = false

                const layerList = document.getElementById('layer-selector')
                layerList.innerHTML = ''

                const asset = document.getElementById('asset-selector').value
                console.log(asset)
                const metadata = scope.metadata[asset]

                //1 band
                for (var i = 0; i < metadata.band_descriptions.length; i++) {
                    let l = document.createElement('option')
                    l.value = metadata.band_descriptions[i][0]
                    l.setAttribute('data-indexes', metadata.band_descriptions[i][0].toString())
                    l.text = metadata.band_descriptions[i][1]
                    layerList.appendChild(l)
                }

                // 3 bands
                for (var i = 0; i < metadata.band_descriptions.length; i++) {
                    let l = document.createElement('option')
                    l.value = metadata.band_descriptions[i][0]
                    l.setAttribute('data-indexes', metadata.band_descriptions[i][0].toString())
                    l.text = metadata.band_descriptions[i][1]
                    if (i === 0) l.selected = "selected"
                    rList.appendChild(l)
                }

                for (var i = 0; i < metadata.band_descriptions.length; i++) {
                    let l = document.createElement('option')
                    l.value = metadata.band_descriptions[i][0]
                    l.setAttribute('data-indexes', metadata.band_descriptions[i][0].toString())
                    l.text = metadata.band_descriptions[i][1]
                    if (i === 1) l.selected = "selected"
                    gList.appendChild(l)
                }

                for (var i = 0; i < metadata.band_descriptions.length; i++) {
                    let l = document.createElement('option')
                    l.value = metadata.band_descriptions[i][0]
                    l.setAttribute('data-indexes', metadata.band_descriptions[i][0].toString())
                    l.text = metadata.band_descriptions[i][1]
                    l.selected = "selected"
                    bList.appendChild(l)
                }

                if (['uint8', 'int8', 'byte'].indexOf(metadata.dtype) === -1) {
                    document.getElementById('rescale-section').classList.remove('none')
                } else {
                    document.getElementById('rescale-section').classList.add('none')
                }

                const bounds = metadata.bounds
                map.fitBounds([[bounds[0], bounds[1]], [bounds[2], bounds[3]]])
                addAOI(bounds)

                if (metadata.band_descriptions.length === 1) {
                    document.getElementById('3b').classList.add('disabled')
                    document.getElementById('3b').classList.remove('active')
                    document.getElementById('3b-section').classList.remove('active')
                    document.getElementById('1b').classList.add('active')
                    document.getElementById('1b-section').classList.add('active')
                } else {
                    document.getElementById('3b').classList.remove('disabled')
                }
            }

            switchViz()
        }
        
        const loadInfo = (assets) => {
            return fetch(`${metadata_endpoint}?url=${scope.url}&assets=${assets}`)
                .then(res => {
                    if (res.ok) return res.json()
                    throw new Error('Network response was not ok.')
                })
                .then(data => {
                    console.log(data)
                    scope.metadata = data
                    return
                })
                .catch(err => {
                    console.warn(err)
                })            
            
        }

        const loadStac = () => {
            return fetch(`${metadata_endpoint}?url=${scope.url}`)
                .then(res => {
                    if (res.ok) return res.json()
                    throw new Error('Network response was not ok.')
                })
                .then(data => {
                    console.log(data)
                    scope.assets = data

                    const layerList = document.getElementById('asset-selector')
                    for (var i = 0; i < scope.assets.length; i++) {
                        let l = document.createElement('option')
                        l.value = scope.assets[i]
                        l.setAttribute('data-asset', scope.assets[i])
                        l.text = scope.assets[i]
                        layerList.appendChild(l)
                    }

                    document.getElementById('hide-arrow').classList.toggle('off')
                    document.getElementById('menu').classList.toggle('off')
                    return scope.assets.join(",")
                })
                .then((assets) => {
                    return loadInfo(assets)
                })
                .then(() => {
                    return updateUI()
                })
                .catch(err => {
                    console.warn(err)
                })            
        }
  
        document.getElementById('launch').addEventListener('click', () => {
            scope.url = document.getElementById('stac').value
            document.getElementById('selector').classList.toggle('none')
            loadStac()
        })

        map.on('load', () => {
            const params = parseParams(window.location.search)
            if (params.url) {
                scope.url = params.url
                document.getElementById('selector').classList.toggle('none')
                loadStac()
            }
            document.getElementById('loader').classList.add('off')
        })

    </script>

</body>

</html>
