module Chemistry.Element ( Element
                         , atomicNumber
                         , element
                         , elementByAtomNumber
                         , shellElectrons
                         , valenceElectronCount
                         , valenceBoundCount 
                         ) where



data Element = Element { atomicNumber :: Int
                        , symbol :: String
                        , _name :: String
                        , _weight :: Double } 
                        deriving Show
            
elements :: [Element]
elements = [ Element 1 "H" "Hydrogen" 1.008 
           , Element 2 "He" "Helium" 4.002602 
           , Element 3 "Li" "Lithium" 6.941 
           , Element 4 "Be" "Beryllium" 9.012182 
           , Element 5 "B" "Boron" 10.811 
           , Element 6 "C" "Carbon" 12.011 
           , Element 7 "N" "Nitrogen" 14.007 
           , Element 8 "O" "Oxygen" 15.999
           , Element 9 "F" "Fluorine" 18.9984032 
           , Element 10 "Ne" "Neon" 20.1797 
           , Element 11 "Na" "Sodium" 22.989768 
           , Element 12 "Mg" "Magnesium" 24.305 
           , Element 13 "Al" "Alluminium" 26.981
           , Element 14 "Si" "Silicon" 28.085
           , Element 15 "P" "Phosphous" 30.973
           , Element 16 "S" "Sulphur" 32.066 
           , Element 17 "Cl" "Chlorine" 35.4527 
           , Element 18 "Ar" "Argon" 39.948 
           , Element 19 "K" "Potassium" 39.0983 
           , Element 20 "Ca" "Calcium" 40.078 
           , Element 21 "Sc" "Scandium" 44.95591 
           , Element 22 "Ti" "Titanium" 47.88 
           , Element 23 "V" "Vanadium" 50.9415 
           , Element 24 "Cr" "Chromium" 51.9961 
           , Element 25 "Mn" "Manganese" 54.93805 
           , Element 26 "Fe" "Iron" 55.845
           , Element 27 "Co" "Cobalt" 58.933
           , Element 28 "Ni" "Nickel" 58.6934
           , Element 29 "Cu" "Copper" 63.546
            -- Zn  Zinc        30  65,39 
            -- Ga  Gallium         31  69,723 
            -- Ge  Germanium       32  72,61 
            -- As  Arsenic         33  74,92159 
            -- Se  Selenium        34  78,96 
            -- Br  Bromine     35  79,904 
            -- Kr  Krypton         36  83,8 
            -- Rb  Rubidium        37  85,4678 
            -- Sr  Strontium       38  87,62 
            -- Y   Yttrium         39  88,90585 
            -- Zr  Zirconium       40  91,224 
            -- Nb  Niobium         41  92,90638 
            -- Mo  Molybdenum      42  95,94 
            , Element  43 "Tc" "Technetium" 98.9063 
            -- Ru  Ruthenium       44  101,07 
            -- Rh  Rhodium         45  102,9055 
            -- Pd  Palladium       46  106,42 
            -- Ag  Silver  47  107,8682 
            -- Cd  Cadmium         48  112,411 
            -- In  Indium      49  114,82 
            -- Sn  Tin     Latin Stannum   50  118,71 
            -- Sb  Antimony   51  121,75 
            -- Te  Tellurium       52  127,6 
            -- I   Iodine      53  126,90447 
            -- Xe  Xenon       54  131,29 
            , Element 55 "Cs" "Caesium" 132.90543 
            , Element 56 "Ba" "Barium" 137.327 
            , Element 57 "La" "Lanthanum" 138.9055 
            , Element 58 "Ce" "Cerium" 140.115 
            , Element 59 "Pr" "Praseodymium" 140.90765 
            , Element 60 "Nd" "Neodymium" 144.24 
            , Element 61 "Pm" "Promethium" 146.9151 
            -- Sm  Samarium        62  150,36 
            -- Eu  Europium        63  151,965 
            -- Gd  Gadolinium      64  157,25 
            -- Tb  Terbium         65  158,92534 
            -- Dy  Dysprosium      66  162,5 
            -- Ho  Holmium         67  164,93032 
            -- Er  Erbium      68  167,26 
            -- Tm  Thulium         69  168,93421 
            -- Yb  Ytterbium       70  173,04 
            -- Lu  Lutetium        71  174,967 
            -- Hf  Hafnium         72  178,49 
            , Element  73 "Ta" "Tantalum" 180.9479 
            -- W   Tungsten    German Wolfram  74  183,85 
            -- Re  Rhenium         75  186,207 
            -- Os  Osmium      76  190,2 
            -- Ir  Iridium         77  192,22 
            -- Pt  Platinum        78  195,08 
            -- Au  Gold    Latin Aurum     79  196,96654  
           , Element 80 "Hg" "Mercury" 200.59  
            -- Tl  Thallium        81  204,3833 
            -- Pb  Lead    Latin Plumbum   82  207,2 
            -- Bi  Bismuth         83  208,98037 
            -- Po  Polonium        84  208,9824 
            -- At  Astatine        85  209,9871 
            , Element 86 "Rn" "Radon" 222.0176 
            -- Fr  Francium        87  223,0197 
            -- Ra  Radium      88  226,0254 
            -- Ac  Actinium        89  227,0278 
            , Element 90 "Th" "Thorium" 232.0381 
            -- Pa  Protactinium        91  231,0359 
            -- U   Uranium         92  238,0289 
            -- Np  Neptunium       93  237,0482 
            -- Pu  Plutonium       94  244,0642 
            -- Am  Americium       95  243,0614 
            -- Cm  Curium      96  247,0703 
            , Element 97 "Bk" "Berkelium" 247.0703 
            -- Cf  Californium         98  251,0796 
            -- Es  Einsteinium         99  252,0829 
            -- Fm  Fermium         100 257,0951 
            -- Md  Mendelevium         101 258,0986 
            -- No  Nobelium        102 259,1009 
            -- Lr  Lawrencium      103 260,1053 
            , Element  104 "Rf" "Rutherfordium" 261.1087 
            -- Db  Dubnium         105 262,1138 
            -- Sg  Seaborgium      106 263,1182 
            -- Bh  Bohrium         107 262,1229 
            -- Hs  Hassium         108 265 
            -- Mt  Meitnerium      109 266 
            -- Ds  Darmstadtium        110 269 
            -- Rg  Roentgenium         111 272 
            , Element 112 "Cn" "Copernicium" 285 
            , Element 113 "Uut" "Ununtrium" 284 
            , Element 114 "Uug" "Ununquadium" 289 
            , Element 115 "Uup" "Ununpentium" 288 
            , Element 116 "Uuh" "Ununhexium" 293 
            , Element 117 "Uus" "Ununseptium" 294     
            , Element 118 "Uuo" "Ununoctium" 294 ]
           

-- | Find element by atomic its number
elementByAtomNumber :: Int -> Maybe Element
elementByAtomNumber n = f n elements
    where f :: Int -> [Element] -> Maybe Element
          f _ [] = Nothing
          f x (e:es) | (atomicNumber e) == x = Just e
                     | otherwise = f x es

-- | Find element by its symbol
element :: String -> Maybe Element
element ns = f ns elements
    where f :: String -> [Element] -> Maybe Element
          f _ [] = Nothing
          f xs (e:es) | (symbol e) == xs = Just e
                     | otherwise = f xs es

-- | Show number of electrons in each shell
shellElectrons :: Element -> [Int]
shellElectrons e = filter (> 0) $ f (fillShells (atomicNumber e)) 
        where f :: [(Int, Int, Int)] -> [Int]
              f ss = [sum (g n ss) | n <- [1..m]]
                where m = ((length ss) + 1) `div` 2
                      g l = map (\(a,_,c) -> if a == l then c else 0)
                      
-- | Number of valance electrons
valenceElectronCount :: Element -> Int
valenceElectronCount e = head $ reverse (shellElectrons e)

-- | Number of bounds in element
valenceBoundCount :: Element -> Int
valenceBoundCount e = min n (8-n)
    where n = valenceElectronCount e


-- How many electrons are in each subshell
subshellMaxElectrons :: [Int]
subshellMaxElectrons = [2, 6, 10, 14, 18]

-- Generate possible config for given maximum number of shells.
-- The order is based on Aufbau principle. 
-- http://en.wikipedia.org/wiki/Aufbau_principle
shellConfigGen :: Int -> [(Int, Int)]
shellConfigGen n = [(i,m-i) | m <- [2..n+n], i <- [((m+1) `div` 2)..m-1] ] 

-- Fill shells with given number of electrons
fillShells :: Int -> [( Int  -- Shell number
                      , Int  -- Shell type
                      , Int  -- #Electrons
                      )]
fillShells n = f (shellConfigGen 5) n
        where f :: [(Int, Int)] -> Int -> [(Int, Int, Int)]
              f [] _ = []
              f ((i,j):xs) m | m == 0 = []
                             | m < l = [(i, j, m)]
                             | otherwise = (i, j, l) : (f xs (m-l))
                             where l = subshellMaxElectrons !! (j-1)