package org.wso2.carbon.device.mgt.mobile.impl.ios;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.device.mgt.common.*;
import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfiguration;
import org.wso2.carbon.device.mgt.common.license.mgt.License;
import org.wso2.carbon.device.mgt.common.license.mgt.LicenseManagementException;
import org.wso2.carbon.device.mgt.common.license.mgt.LicenseManager;
import org.wso2.carbon.device.mgt.mobile.impl.ios.dao.AbstractMobileDeviceManagementDAOFactory;
import org.wso2.carbon.device.mgt.mobile.impl.ios.dao.IOSDAOFactory;
import org.wso2.carbon.device.mgt.mobile.impl.ios.dao.MobileDeviceManagementDAOException;
import org.wso2.carbon.device.mgt.mobile.impl.ios.dao.impl.IOSDeviceMgtPluginException;
import org.wso2.carbon.device.mgt.mobile.impl.ios.dto.MobileDevice;
import org.wso2.carbon.device.mgt.mobile.impl.ios.util.IOSPluginConstants;
import org.wso2.carbon.device.mgt.mobile.impl.ios.util.IOSUtils;
import org.wso2.carbon.device.mgt.mobile.impl.ios.util.MobileDeviceManagementUtil;
import org.wso2.carbon.registry.api.RegistryException;
import org.wso2.carbon.registry.api.Resource;
import org.wso2.carbon.device.mgt.extensions.license.mgt.registry.RegistryBasedLicenseManager;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.StringReader;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.util.List;

/**
 * This represents the iOS implementation of DeviceManagerService.
 */
public class IOSDeviceManager implements DeviceManager {

    private static final Log log = LogFactory.getLog(IOSDeviceManager.class);
    private AbstractMobileDeviceManagementDAOFactory mobileDeviceManagementDAOFactory;
    private IOSFeatureManager iosFeatureManager;
    private LicenseManager licenseManager;

    public IOSDeviceManager() {
        mobileDeviceManagementDAOFactory = new IOSDAOFactory();
        iosFeatureManager = new IOSFeatureManager();
        this.licenseManager = new RegistryBasedLicenseManager();
        try {
            if (licenseManager.getLicense(DeviceManagementConstants.MobileDeviceTypes.MOBILE_DEVICE_TYPE_IOS,
                    DeviceManagementConstants.LanguageCodes.LANGUAGE_CODE_ENGLISH_US) == null) {
                License defaultLicense = IOSUtils.getDefaultLicense();
                licenseManager.addLicense(DeviceManagementConstants.
                        MobileDeviceTypes.MOBILE_DEVICE_TYPE_IOS, defaultLicense);
            }
            iosFeatureManager.addSupportedFeaturesToDB();
        } catch (LicenseManagementException e) {
            String msg = "Error occurred while adding default license for iOS devices.";
            log.error(msg, e);
            throw new IllegalStateException(msg, e);
        } catch (DeviceManagementException e) {
            String msg = "Error occurred while adding supported device features for ios platform.";
            log.error(msg, e);
            throw new IllegalStateException(msg, e);
        }
    }

    public FeatureManager getFeatureManager() {
        return iosFeatureManager;
    }

    @Override
    public boolean saveConfiguration(PlatformConfiguration tenantConfiguration)
            throws DeviceManagementException {
        boolean status;
        Resource resource;
        try {
            if (log.isDebugEnabled()) {
                log.debug("Persisting iOS configurations in Registry");
            }
            String resourcePath = MobileDeviceManagementUtil.getPlatformConfigPath(
                    DeviceManagementConstants.
                            MobileDeviceTypes.MOBILE_DEVICE_TYPE_IOS);
            StringWriter writer = new StringWriter();
            JAXBContext context = JAXBContext.newInstance(PlatformConfiguration.class);
            Marshaller marshaller = context.createMarshaller();
            marshaller.marshal(tenantConfiguration, writer);

            resource = MobileDeviceManagementUtil.getConfigurationRegistry().newResource();
            resource.setContent(writer.toString());
            resource.setMediaType(IOSPluginConstants.MobilePluginConstants.MEDIA_TYPE_XML);
            MobileDeviceManagementUtil.putRegistryResource(resourcePath, resource);
            status = true;
        } catch (IOSDeviceMgtPluginException e) {
            throw new DeviceManagementException(
                    "Error occurred while retrieving the Registry instance.", e);
        } catch (RegistryException e) {
            throw new DeviceManagementException(
                    "Error occurred while persisting the Registry resource of iOS Configuration.", e);
        } catch (JAXBException e) {
            throw new DeviceManagementException(
                    "Error occurred while parsing the iOS configuration.", e);
        }
        return status;
    }

    @Override
    public PlatformConfiguration getConfiguration() throws DeviceManagementException {
        Resource resource;
        try {
            String iOSRegPath =
                    MobileDeviceManagementUtil.getPlatformConfigPath(DeviceManagementConstants.
                            MobileDeviceTypes.MOBILE_DEVICE_TYPE_IOS);
            resource = MobileDeviceManagementUtil.getRegistryResource(iOSRegPath);

            if (resource == null) {
                return new PlatformConfiguration();
            }

            JAXBContext context = JAXBContext.newInstance(PlatformConfiguration.class);
            Unmarshaller unmarshaller = context.createUnmarshaller();
            return (PlatformConfiguration) unmarshaller.unmarshal(
                    new StringReader(new String((byte[]) resource.getContent(), Charset
                            .forName(IOSPluginConstants.MobilePluginConstants.CHARSET_UTF8))));

        } catch (IOSDeviceMgtPluginException e) {
            throw new DeviceManagementException(
                    "Error occurred while retrieving the Registry instance.", e);
        } catch (JAXBException e) {
            throw new DeviceManagementException(
                    "Error occurred while parsing the iOS configuration.", e);
        } catch (RegistryException e) {
            throw new DeviceManagementException(
                    "Error occurred while retrieving the Registry resource of iOS Configuration.", e);
        }
    }

    public boolean enrollDevice(Device device) throws DeviceManagementException {
        boolean status;
        MobileDevice mobileDevice = MobileDeviceManagementUtil.convertToMobileDevice(device);
        try {
            boolean isEnrolled = this.isEnrolled(
                    new DeviceIdentifier(device.getDeviceIdentifier(), device.getType()));

            if (isEnrolled) {
                status = this.modifyEnrollment(device);
            } else {
                IOSDAOFactory.beginTransaction();
                status = mobileDeviceManagementDAOFactory.getMobileDeviceDAO().addMobileDevice(
                        mobileDevice);
                IOSDAOFactory.commitTransaction();
            }
        } catch (MobileDeviceManagementDAOException e) {
            try {
                IOSDAOFactory.rollbackTransaction();
            } catch (MobileDeviceManagementDAOException e1) {
                String msg = "Error occurred while rolling back the IOS device add transaction : " +
                        device.getDeviceIdentifier();
                log.warn(msg, e);
            }
            String msg = "Error occurred while enrolling the iOS device : " + device.getDeviceIdentifier();
            log.error(msg, e);
            throw new DeviceManagementException(msg, e);
        }
        return status;
    }

    public boolean modifyEnrollment(Device device) throws DeviceManagementException {
        boolean status;
        MobileDevice mobileDevice = MobileDeviceManagementUtil.convertToMobileDevice(device);
        try {
            IOSDAOFactory.beginTransaction();
            if (log.isDebugEnabled()) {
                log.debug("Modifying the iOS device enrollment data");
            }
            status = mobileDeviceManagementDAOFactory.getMobileDeviceDAO()
                    .updateMobileDevice(mobileDevice);
            IOSDAOFactory.commitTransaction();
        } catch (MobileDeviceManagementDAOException e) {
            try {
                IOSDAOFactory.rollbackTransaction();
            } catch (MobileDeviceManagementDAOException mobileDaoEx) {
                String msg = "Error occurred while rolling back the modify enrol transaction: " + device.getId();
                log.warn(msg, mobileDaoEx);
            }
            String msg = "Error occurred while updating the enrollment of the iOS device : " +
                    device.getDeviceIdentifier();
            log.error(msg, e);
            throw new DeviceManagementException(msg, e);
        }
        return status;
    }

    public boolean disenrollDevice(DeviceIdentifier deviceId) throws DeviceManagementException {
        return true;
    }

    public boolean isEnrolled(DeviceIdentifier deviceId) throws DeviceManagementException {
        boolean isEnrolled = false;
        try {
            if (log.isDebugEnabled()) {
                log.debug("Checking the enrollment of iOS device : " + deviceId.getId());
            }
            MobileDevice mobileDevice =
                    mobileDeviceManagementDAOFactory.getMobileDeviceDAO().getMobileDevice(
                            deviceId.getId());
            if (mobileDevice != null) {
                isEnrolled = true;
            }
        } catch (MobileDeviceManagementDAOException e) {
            String msg = "Error occurred while checking the enrollment status of iOS device : " +
                    deviceId.getId();
            log.error(msg, e);
            throw new DeviceManagementException(msg, e);
        }
        return isEnrolled;
    }

    public boolean isActive(DeviceIdentifier deviceId) throws DeviceManagementException {
        return true;
    }

    public boolean setActive(DeviceIdentifier deviceId, boolean status)
            throws DeviceManagementException {
        return true;
    }

    public List<Device> getAllDevices() throws DeviceManagementException {
        return null;
    }

    public Device getDevice(DeviceIdentifier deviceId) throws DeviceManagementException {
        Device device;
        try {
            if (log.isDebugEnabled()) {
                log.debug("Getting the details of iOS device : " + deviceId.getId());
            }
            MobileDevice mobileDevice = mobileDeviceManagementDAOFactory.getMobileDeviceDAO().
                    getMobileDevice(deviceId.getId());
            device = MobileDeviceManagementUtil.convertToDevice(mobileDevice);
        } catch (MobileDeviceManagementDAOException e) {
            String msg = "Error occurred while fetching the iOS device : " + deviceId.getId();
            log.error(msg, e);
            throw new DeviceManagementException(msg, e);
        }
        return device;
    }

    public boolean setOwnership(DeviceIdentifier deviceId, String ownershipType) throws DeviceManagementException {
        return true;
    }

    public boolean isClaimable(DeviceIdentifier deviceIdentifier) throws DeviceManagementException {
        return false;
    }

    public boolean setStatus(DeviceIdentifier deviceIdentifier, String s,
                             EnrolmentInfo.Status status) throws DeviceManagementException {
        return false;
    }

    @Override
    public License getLicense(String languageCode) throws LicenseManagementException {
        return licenseManager.getLicense(DeviceManagementConstants.MobileDeviceTypes.MOBILE_DEVICE_TYPE_IOS,
                languageCode);
    }

    @Override
    public void addLicense(License license) throws LicenseManagementException {
        licenseManager.addLicense(DeviceManagementConstants.MobileDeviceTypes.MOBILE_DEVICE_TYPE_IOS, license);
    }

    @Override
    public boolean requireDeviceAuthorization() {
        return false;
    }

    public boolean updateDeviceInfo(DeviceIdentifier deviceIdentifier, Device device) throws DeviceManagementException {
        boolean status;
        MobileDevice mobileDevice = MobileDeviceManagementUtil.convertToMobileDevice(device);
        try {
            IOSDAOFactory.beginTransaction();
            if (log.isDebugEnabled()) {
                log.debug("Modifying the iOS device enrollment data");
            }
            status = mobileDeviceManagementDAOFactory.getMobileDeviceDAO().updateMobileDevice(mobileDevice);
            IOSDAOFactory.commitTransaction();
        } catch (MobileDeviceManagementDAOException e) {
            try {
                IOSDAOFactory.rollbackTransaction();
            } catch (MobileDeviceManagementDAOException mobileDaoEx) {
                String msg = "Error occurred while rolling back the modify enrol transaction : " + device.getId();
                log.warn(msg, mobileDaoEx);
            }
            String msg = "Error occurred while updating the enrollment of the iOS device : " +
                    device.getDeviceIdentifier();
            log.error(msg, e);
            throw new DeviceManagementException(msg, e);
        }
        return status;
    }

}
