import io
import sys

from iotbx.cif import CifParserError, reader


def read_cif(f, verbose=False):
    """opens cif and returns cctbx data object"""
    try:
        if isinstance(f, io.IOBase):
            structures = reader(file_object=f).build_crystal_structures()
        elif isinstance(f, str):
            structures = reader(file_path=f).build_crystal_structures()
        else:
            raise TypeError(f'read_cif: Can not deal with type {type(f)}')
    except CifParserError as e:
        print(e)
        print('Error parsing cif file, check if the data tag does not contain any spaces.')
        sys.exit()
    if verbose:
        for key, val in list(structures.items()):
            print('\nstructure:', key)
            val.show_summary().show_scatterers()
    return structures


def main():
    cifs = sys.argv[1:]

    if len(cifs) == 0:
        print('cif2topas - tool for converting cif files to Topas format (.inp)')
        print('Takes any number of cif files and writes to stdout')
        print('')
        print('usage: cif2topas cif1 [cif2 ...]')
        sys.exit()

    for cif in cifs:
        # print 'Reading file: {}'.format(cif)
        structures = read_cif(cif)
        for name in structures:
            structure = structures[name].sites_mod_positive()
            sg = structure.space_group()
            uc = structure.unit_cell()
            sps = structure.special_position_settings()
            scatterers = structure.scatterers()

            print("""
   str
      Out_X_Yobs(x_yobs.xy)
      Out_X_Ycalc(x_ycalc.xy)
      Out_X_Difference(x_ydiff.xy)
      Create_2Th_Ip_file(ticks.out)

      Out_fobs(fobs.hkl)

      Out_CIF_STR(structure.cif)""")

            print("""
      scale  @  1.0
      r_bragg  1.0""")
            print()

            s400 = "      'prm s400 1.0 \n"
            s040 = "      'prm s040 1.0 \n"
            s004 = "      'prm s004 1.0 \n"
            s220 = "      'prm s220 1.0 \n"
            s202 = "      'prm s202 1.0 \n"
            s022 = "      'prm s022 1.0 \n"
            s301 = "      'prm s301 1.0 \n"
            s121 = "      'prm s121 1.0 \n"
            s103 = "      'prm s103 1.0 \n"
            eta = "      'prm !eta 0.5 min 0.0 max 1.0\n"
            if sg.crystal_system() == 'Monoclinic':
                string = f'{s400}{s040}{s004}{s220}{s202}{s022}{s301}{s121}{s103}{eta}'
                macro = (
                    "      'Stephens_monoclinic(s400, s040, s004, s220, "
                    's202, s022, s301, s121, s103, eta)'
                )

            elif sg.crystal_system() == 'Tetragonal':
                string = f'{s400}{s004}{s220}{s202}{eta}'
                macro = "      'Stephens_tetragonal(s400, s004, s220, s202, eta)"

            elif sg.crystal_system() == 'Hexagonal':
                string = f'{s400}{s004}{s202}{eta}'
                macro = "      'Stephens_hexagonal(s400, s202, s004, eta)"

            elif sg.crystal_system() == 'Orthorhombic':
                string = f'{s400}{s040}{s004}{s220}{s202}{s022}{eta}'
                macro = "      'Stephens_orthorhombic(s400, s040, s004, s220, s202, s022, eta)"
            else:
                string = ''
                macro = ''
            print(string, end=' ')
            print(macro)

            print("""
      PV_Peak_Type(
      ha,    0.02,
      !hb,   0.0,
      !hc,   0.0,
      lora,  0.5,
      !lorb, 0.0,
      !lorc, 0.0)

      'Simple_Axial_Model(axial, 0.0)

      Phase_Density_g_on_cm3( 1.0)""")

            print("""
      view_structure

      'fourier_map 1
      '   fourier_map_formula = Fobs - Fcalc;""")
            print()
            print(
                '      space_group "{}"'.format(
                    sg.type().universal_hermann_mauguin_symbol().replace(' ', '')
                )
            )
            print()
            a, b, c, al, be, ga = uc.parameters()
            if sg.crystal_system() == 'Cubic':
                string = '      Cubic(@ {a})'

            elif sg.crystal_system() == 'Tetragonal':
                string = '      Tetragonal(@ {a}, @ {c})'

            elif sg.crystal_system() == 'Hexagonal':
                string = '      Hexagonal(@ {a}, @ {c})'

            elif sg.crystal_system() == 'Rhombohedral':
                string = '      Rhombohedral(@ {a}, @ {al})'
            else:
                string = """      a  @  {a:8.5f}
      b  @  {b:8.5f}
      c  @  {c:8.5f}
      al {refal:1}  {al}
      be {refbe:1}  {be}
      ga {refga:1}  {ga}"""
            refal = '' if al == 90 else '@'
            refbe = '' if be == 90 else '@'
            refga = '' if ga == 90 else '@'
            print(
                string.format(
                    a=a, b=b, c=c, refal=refal, al=al, refbe=refbe, be=be, refga=refga, ga=ga
                )
            )
            print()
            print(f'      volume {uc.volume():.2f}')
            print()
            for element in set(scatterers.extract_scattering_types()):
                print(f'      prm beq{element:2s}  2.0  min 1.0  max 5.0')
            print()

            z_order = sg.order_z()

            for atom in scatterers:
                label = atom.label
                x, y, z = atom.site
                atom.element_symbol()
                element = atom.element_symbol()
                mult = atom.multiplicity()

                print(
                    (
                        f'      site {label:5s}  num_posns {mult:3d}  x  {x:.5f}  y  {y:.5f}  '
                        f'z  {z:.5f}  occ {element:2s}  1.0  beq =beq{element:2s};'
                    ),
                    end=' ',
                )

                if mult < z_order:
                    print(
                        "   ' {:5s} {:5s} {:5s}".format(
                            *sps.site_symmetry(atom.site).special_op_simplified().terms
                        )
                    )
                else:
                    print()


if __name__ == '__main__':
    main()
