module Main where

import Common

import qualified Sound.ALSA.Sequencer.Event as Event
import qualified Sound.ALSA.Sequencer as SndSeq

import Graphics.UI.WX
   ((.+.), Prop((:=)), set, get, selection, command, on,
    button, close, container, hfloatCentre, widget, vfill,
    layout, margin, select, spinCtrl, text, label, )

import qualified Graphics.UI.WX as WX
import qualified Graphics.UI.WXCore as WXCore

import Control.Monad (forM, forM_, when, )
import qualified Data.List as List


wxSL_INVERSE :: Int
wxSL_INVERSE = 0x1000

vslider ::
   WX.Window a -> Bool -> Int -> Int ->
   [WX.Prop (WX.Slider ())] -> IO (WX.Slider ())
vslider parentW showLabels top bottom props =
   let (minV, maxV, dirFlags) =
          if top<bottom
            then (top, bottom, 0)
            else (bottom, top, wxSL_INVERSE)
   in  WX.sliderEx parentW minV maxV
          (WXCore.wxVERTICAL .+. dirFlags -- .+. wxSL_LEFT .+. wxSL_AUTOTICKS
            .+. (if showLabels then WXCore.wxSL_LABELS else 0))
          props

transferSelection ::
   (WX.Selection a, WX.Selection b) =>
   WX.Event a (IO ()) -> (Event.Value -> IO ()) -> a -> b -> IO ()
transferSelection event action src dst =
   set src [on event :=
      get src selection >>= \i -> set dst [selection := i] >> action (Event.Value $ fromIntegral i)]

getController :: WX.SpinCtrl () -> IO Event.Parameter
getController ctrlSpin =
   fmap (Event.Parameter . fromIntegral) $
   WX.get ctrlSpin WX.selection


data Slider =
   Slider (WX.Slider ()) (WX.SpinCtrl ()) (WX.SpinCtrl ()) (WX.SpinCtrl ())

updateSliders :: [Slider] -> Event.T -> IO ()
updateSliders sliders ev =
   case Event.body ev of
      Event.CtrlEv Event.Controller ctrlEv ->
         forM_ sliders $ \(Slider val sval ctrl chan) -> do
            midiChan <- getChannel chan
            midiCtrl <- getController ctrl
            when (midiChan == Event.ctrlChannel ctrlEv &&
                  midiCtrl == Event.ctrlParam ctrlEv) $
               let v = fromIntegral $ Event.unValue $ Event.ctrlValue ctrlEv
               in  set val [selection := v] >>
                   set sval [selection := v]
      _ -> return ()

makeGUI :: Sequencer SndSeq.DuplexMode -> IO ()
makeGUI sequ = do
   f <- WX.frame [text := "MIDI Controllers"]
   p <- WX.panel f []
   sliders <- forM [7, 1, 73, 70, 71, 93, 94, 95] $ \n -> do
      val  <- vslider p False 127 0 []
      sval <- spinCtrl p 0 127 []
      ctrl <- spinCtrl p 0 119 [selection := n]
      chan <- spinCtrl p 0 15 []
      let send x = do
             midiChan <- getChannel chan
             midiCtrl <- getController ctrl
             sendCtrl sequ midiChan midiCtrl x
      transferSelection command send val sval
      transferSelection select send sval val
      return $ Slider val sval ctrl chan
   reactOnEvent 20 f sequ (updateSliders sliders)
   quit <- button p [text := "Quit", on command := close f]
   let makeCol (Slider val sval ctrl chan) =
          vfill (widget val) :
          widget sval :
          widget ctrl :
          widget chan :
          []
       labels =
          map WX.valignCentre $
          WX.vglue :
          label "Value" :
          label "Controller" :
          label "Channel" :
          []
   set f [layout := container p $ margin 10 $
             WX.column 5 [
                WX.grid 5 5 $ List.transpose $
                labels : map makeCol sliders,
                hfloatCentre (widget quit)]]


sendCtrl ::
   Sequencer SndSeq.DuplexMode ->
   Event.Channel -> Event.Parameter -> Event.Value -> IO ()
sendCtrl h chan ctrl val =
   sendEvent h $
      Event.CtrlEv Event.Controller $ Event.Ctrl {
         Event.ctrlChannel = chan,
         Event.ctrlParam = ctrl,
         Event.ctrlValue = val
      }


main :: IO ()
main =
   withSequencer "Slider bank" $ WX.start . makeGUI
