#define FULL_TEST //- code surrounded is not to be included in the documentation.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Web.Configuration;
using System.Windows.Forms;
using MemoQServicesClient.ServerProjectService;
using System.IO;
using MemoQServicesClient.FileManagerService;
using MemoQServicesClient.TMService;
using MemoQServicesClient.TBService;
using System.ServiceModel;
using MemoQServicesClient.SecurityService;
using MemoQServicesClient.ResourceService;
using LightResourceInfoWithLang = MemoQServicesClient.ResourceService.LightResourceInfoWithLang;
using ResourceType = MemoQServicesClient.ServerProjectService.ResourceType;
using UserInfo = MemoQServicesClient.SecurityService.UserInfo;
using MemoQServicesClient.TasksService;

namespace MemoQServicesClient
{
class ServerProjectServiceTest
{
    private Guid tmGuid1;
    private Guid tmGuid2;
    private Guid tmGuid3;
    private bool areTMsCreated;

    void createTMsIfNotCreated()
    {
        lock (this)
        {
            if (!areTMsCreated)
            {
                areTMsCreated = true;
                // Create TMs, the guid of the newly created TM is retuned
                tmGuid1 = createAndPublishSampleTM("hun", "eng");
                tmGuid2 = createAndPublishSampleTM("hun", "eng");
                tmGuid3 = createAndPublishSampleTM("hun", "ger");
            }
        }
    }

    public string DemoTest()
    {
        // Get server project service object.
        IServerProjectService spService = getServerProjectService();

        // Create server project, the guid of the newly created project
        // is returned.
        // The project will have two target languages: english and german.
        string spName;
        Guid spGuid = createSampleProject(out spName, "hun",
            new string[] {"eng", "ger"}, false);

        //------------ Set TM assignments for both target lang -------------

        // Create TMs, the guid of the newly created TM is retuned
        Guid tmGuid1 = createAndPublishSampleTM("hun", "eng");
        Guid tmGuid2 = createAndPublishSampleTM("hun", "eng");
        Guid tmGuid3 = createAndPublishSampleTM("hun", "ger");

        // Prepare TM assignments for target lang "eng"
        ServerProjectTMAssignmentsForTargetLang tmAssignmentsEng
            = new ServerProjectTMAssignmentsForTargetLang();
        tmAssignmentsEng.TargetLangCode = "eng";
        tmAssignmentsEng.TMGuids = new Guid[] {tmGuid1, tmGuid2};
        tmAssignmentsEng.PrimaryTMGuid = tmGuid1;

        // Prepare TM assignments for target lang "ger"
        ServerProjectTMAssignmentsForTargetLang tmAssignmentsGer
            = new ServerProjectTMAssignmentsForTargetLang();
        tmAssignmentsGer.TargetLangCode = "ger";
        tmAssignmentsGer.TMGuids = new Guid[] { tmGuid3 };
        tmAssignmentsGer.PrimaryTMGuid = tmGuid3;

        ServerProjectTMAssignmentsForTargetLang[] tmAssignments =
          new ServerProjectTMAssignmentsForTargetLang[] { tmAssignmentsEng,
              tmAssignmentsGer };

        // Apply TM assignmenst
        spService.SetProjectTMs2(spGuid, tmAssignments);

        // Create a TB, the guid of the newly created TB is retuned
        Guid tbGuid = createAndPublishSampleTB();
        // Assign the TB to the project
        spService.SetProjectTBs(spGuid, new Guid[] { tbGuid }, tbGuid);

        // Assign the users to the project

        // First, select two random users to be assigned.
        MemoQServicesClient.SecurityService.UserInfo[] allUsers =
            getSecurityService().ListUsers();
        List<MemoQServicesClient.SecurityService.UserInfo> allUsersWithoutAdminAndGuest =
            new List<MemoQServicesClient.SecurityService.UserInfo>();
        foreach (MemoQServicesClient.SecurityService.UserInfo u in allUsers)
            if (u.UserGuid != TestUsers.TestUserAdminGuid && u.UserGuid != TestUsers.TestUserGuestGuid)
                allUsersWithoutAdminAndGuest.Add(u);
        if (allUsersWithoutAdminAndGuest.Count < 2)
            throw new Exception(@"To run this test you have to have
         at least two users on MemoQ Server except admin and guest");
        int indexOfUser = new Random().Next(allUsersWithoutAdminAndGuest.Count - 1);
        Guid userGuidOfRandomUser1 = allUsersWithoutAdminAndGuest[indexOfUser].UserGuid;
        indexOfUser = (indexOfUser + 1) % allUsersWithoutAdminAndGuest.Count;
        Guid userGuidOfRandomUser2 = allUsersWithoutAdminAndGuest[indexOfUser].UserGuid;

        // First, select the built in admin user to be assigned
        // with project role ProjectManager (but not Terminologist)
        ServerProjectUserInfo spui1 = new ServerProjectUserInfo();
        spui1.UserGuid = TestUsers.TestUserAdminGuid;
        spui1.ProjectRoles = new ServerProjectRoles();
        spui1.ProjectRoles.ProjectManager = true;
        spui1.ProjectRoles.Terminologist = false;

        ServerProjectUserInfo spui2 = new ServerProjectUserInfo();
        spui2.UserGuid = userGuidOfRandomUser1;
        spui2.ProjectRoles = new ServerProjectRoles();
        spui2.ProjectRoles.ProjectManager = false;
        spui2.ProjectRoles.Terminologist = false;

        ServerProjectUserInfo spui3 = new ServerProjectUserInfo();
        spui3.UserGuid = userGuidOfRandomUser2;
        spui3.ProjectRoles = new ServerProjectRoles();
        spui3.ProjectRoles.ProjectManager = true;
        spui3.ProjectRoles.Terminologist = true;

        // Apply project-user assignments
        ServerProjectUserInfo[] sampleUserInfos = new ServerProjectUserInfo[]
        {
          spui1, spui2, spui3
        };
        spService.SetProjectUsers(spGuid, sampleUserInfos);

        // Upload documents

        // Make sure this is a valid path
        string pathOfTxt01 = "c:\\_MemoQTest_\\testfiles\\#A#.txt";
        Guid fileGuidTxt01 = uploadSampleFile(pathOfTxt01);
        // Make sure this is a valid path
        string pathOfRtf01 = "c:\\_MemoQTest_\\testfiles\\#G_rtf#.rtf";
        //string pathOfFileB = "c:\\_MemoQTest_\\testfiles\\#H_rtf_biling#.rtf";
        Guid fileGuidRtf01 = uploadSampleFile(pathOfRtf01);
     #if FULL_TEST
        // Make sure this is a valid path
        string pathOfHtml01 = "c:\\_MemoQTest_\\testfiles\\#I_htm#.htm";
        Guid fileGuidHtml01 = uploadSampleFile(pathOfHtml01);
        // Make sure this is a valid path
        string pathOfPdf01 = "c:\\_MemoQTest_\\testfiles\\#M_1#.pdf";
        Guid fileGuidPdf01 = uploadSampleFile(pathOfPdf01);
        // Make sure this is a valid path
        string pathOfDocx01 = "c:\\_MemoQTest_\\testfiles\\#N_A#.docx";
        Guid fileGuidDocx01 = uploadSampleFile(pathOfDocx01);
        string pathOfPptx01 = "c:\\_MemoQTest_\\testfiles\\TRDoc_pptx_01.pptx";
        Guid fileGuidPptx01 = uploadSampleFile(pathOfPptx01);
     #endif

        #region Assign some light resources to the project

        // These steps are optional, as defaults for resource types that
        // are required for the project are always assigned at project
        // creation.
        // To find out what are the rules for the assignment of different
        // resource types see code comments on class
        // ServerProjectResourceAssignmentForResourceType

        // Import a segmentation rule resource and assign it to the project
        IResourceService resourceService = getResourceService();
        // This is a "hun" segrule (has to be as the source lang of the project
        // is "hun".
        string pathOfSegruleRes =
            "c:\\_MemoQTest_\\testfiles\\Res_SegRuleHun.mqres";
        Guid fileGuidSegruleRes = uploadSampleFile(pathOfSegruleRes);
        // SegRule type resources use LightResourceInfoWithLang (derived from
        // LightResourceInfo. But as the language code of a segrule resource
        // can not be changed during import, LightResourceInfo is also accepted.
        ResourceService.LightResourceInfo segRuleResInfo =
            new ResourceService.LightResourceInfo();
        segRuleResInfo.Name = "SegRule_Name_" + Guid.NewGuid().ToString();
        segRuleResInfo.Description = "###unittest###";
        Guid segRuleGuid =
            resourceService.ImportNewAndPublish(
            ResourceService.ResourceType.SegRules, fileGuidSegruleRes,
            segRuleResInfo );

        ServerProjectResourceAssignment aSr1 =
            new ServerProjectResourceAssignment();
        aSr1.ResourceGuid = segRuleGuid;
        // Not required to set these, as null and false are the defaults
        // aSr1.ObjectId = null;
        // aSr1.Primary = false;

        ServerProjectResourceAssignmentForResourceType segRuleAssignments =
             new ServerProjectResourceAssignmentForResourceType();
        segRuleAssignments.ResourceType = ResourceType.SegRules;
        segRuleAssignments.ServerProjectResourceAssignment =
            new ServerProjectResourceAssignment[] { aSr1 };

        // Do not apply the SegRule resource yet. Instead, prepare a
        // TMSetting resource to override project defaults and apply
        // the SegRule and TMSetting in one step (this is slightly more
        // effective),

        string pathOfTMSettingsRes =
            "c:\\_MemoQTest_\\testfiles\\Res_TMSettings01.mqres";
        Guid fileGuidTMSettingsRes = uploadSampleFile(pathOfTMSettingsRes);
        // TMSettings resources use LightResourceInfo objects directly.
        ResourceService.LightResourceInfo tmsResInfo =
            new ResourceService.LightResourceInfo();
        tmsResInfo.Name = "TMSettings_Name_" + Guid.NewGuid().ToString();
        tmsResInfo.Description = "###unittest###";
        Guid tmsGuid =
            resourceService.ImportNewAndPublish(
            ResourceService.ResourceType.TMSettings, fileGuidTMSettingsRes,
            tmsResInfo);
        // Assign deployed default as project default (this was assigned at
        // project creation, but will be deleted at assignment, so we have
        // to reassign it.
        ServerProjectResourceAssignment aTmsProj =
            new ServerProjectResourceAssignment();
        aTmsProj.ResourceGuid = Guid.Empty; // Assignment of deployed default.
        aTmsProj.ObjectId = null; // Assignment for project default.
        // Override TM settings for TM1
        ServerProjectResourceAssignment aTmsTM1 =
            new ServerProjectResourceAssignment();
        aTmsTM1.ResourceGuid = tmsGuid; // Assignment of deployed default.
        aTmsTM1.ObjectId = tmGuid1.ToString(); // Assignment for TM1
        ServerProjectResourceAssignmentForResourceType tmsAssignments =
             new ServerProjectResourceAssignmentForResourceType();
        tmsAssignments.ResourceType = ResourceType.TMSettings;
        tmsAssignments.ServerProjectResourceAssignment =
            new ServerProjectResourceAssignment[] { aTmsProj, aTmsTM1 };
        // Apply the assignments for SegRule and TMSettings in one step
        // (also could be done in two steps)
        spService.SetProjectResourceAssignments(
            spGuid,
            new ServerProjectResourceAssignmentForResourceType[]
            { segRuleAssignments, tmsAssignments }
          );

        #endregion

        //  --- Import document(s) now ---

        TranslationDocImportResultInfo trResultInfo;
        // Import first file based on its extension with default
        // settings into all project target langs.
        trResultInfo = spService.ImportTranslationDocument(spGuid, fileGuidTxt01,
            null, null);
        // Delete file on server, we no longer need it
        getFileManagerService().DeleteFile(fileGuidTxt01);

        Guid docAGuidEng;
        Guid docAGuidGer;

        if (trResultInfo.ResultStatus == ServerProjectService.ResultStatus.Error)
        {
            // Handle error, we have the error message in
            // trResultInfo.MainMessageField
            // and trResultInfo.MainMessageField

            throw new Exception("Document import failed: " +
                trResultInfo.MainMessage);
        }
        else
        {
            // If all went OK...

            // We received the guids of the imported doc for all
            // requested target languages.
            docAGuidEng = trResultInfo.DocumentGuids[0];
            docAGuidGer = trResultInfo.DocumentGuids[1];

            // Just to be fully sure we could examine that none of the
            // received Guids is "00000000-0000-0000-0000-000000000000"
            // We skip this step for simplicity.

            // Assigne doc to users with requested document assignment
            // role (Translator/Reviewer1/reviewer2) and deadlines.

            // First prepare assigmnent of docA target lang "eng"
            ServerProjectTranslationDocumentUserAssignments spdaDocAEng =
                new ServerProjectTranslationDocumentUserAssignments();
            spdaDocAEng.DocumentGuid = docAGuidEng;
            spdaDocAEng.UserRoleAssignments =
                new TranslationDocumentUserRoleAssignment[3];

            // Assign admin to the as Translator document assignment role
            spdaDocAEng.UserRoleAssignments[0] =
                new TranslationDocumentUserRoleAssignment();
            spdaDocAEng.UserRoleAssignments[0].UserGuid =
                TestUsers.TestUserAdminGuid;
            spdaDocAEng.UserRoleAssignments[0].DocumentAssignmentRole = 0;
            spdaDocAEng.UserRoleAssignments[0].DeadLine = new DateTime(2010, 5, 1);

            // Assign random user to the as Reviewer1 document assignment role
            // with no deadline
            spdaDocAEng.UserRoleAssignments[1] =
                new TranslationDocumentUserRoleAssignment();
            spdaDocAEng.UserRoleAssignments[1].UserGuid = userGuidOfRandomUser1;
            spdaDocAEng.UserRoleAssignments[1].DocumentAssignmentRole = 1;
            spdaDocAEng.UserRoleAssignments[1].DeadLine = new DateTime(2500, 1, 1);

            // Assign another random user to the as Reviewer2 document assignment role
            // with no deadline
            spdaDocAEng.UserRoleAssignments[2] =
                new TranslationDocumentUserRoleAssignment();
            spdaDocAEng.UserRoleAssignments[2].UserGuid = userGuidOfRandomUser2;
            spdaDocAEng.UserRoleAssignments[2].DocumentAssignmentRole = 2;
            spdaDocAEng.UserRoleAssignments[2].DeadLine = new DateTime(2500, 1, 1);

            // Next prepare assigmnent of docA target lang "ger"

            ServerProjectTranslationDocumentUserAssignments spdaDocAGer =
                new ServerProjectTranslationDocumentUserAssignments();
            spdaDocAGer.DocumentGuid = docAGuidGer;
            spdaDocAGer.UserRoleAssignments =
                new TranslationDocumentUserRoleAssignment[1];

            // Assign admin to the as Translator document assignment role
            spdaDocAGer.UserRoleAssignments[0] =
                new TranslationDocumentUserRoleAssignment();
            spdaDocAGer.UserRoleAssignments[0].UserGuid =
                TestUsers.TestUserAdminGuid;
            spdaDocAGer.UserRoleAssignments[0].DocumentAssignmentRole = 0;
            spdaDocAGer.UserRoleAssignments[0].DeadLine = new DateTime(2010, 5, 1);

            // We assign no user to the Reviewer1 and Reviewer2 roles for
            // target lang "ger"

            // Now save the assignment we have prepared
            ServerProjectTranslationDocumentUserAssignments[] documentUserAssignments =
                new ServerProjectTranslationDocumentUserAssignments[]
            {
                spdaDocAEng, spdaDocAGer
            };

            spService.SetProjectTranslationDocumentUserAssignments(spGuid,
                documentUserAssignments);

        }

        // Import next file based on its extension with default
        // settings into target lang english only.
        trResultInfo = spService.ImportTranslationDocument(spGuid, fileGuidRtf01,
            new string[] {"eng"}, null );
        if (trResultInfo.ResultStatus == ServerProjectService.ResultStatus.Error)
            throw new Exception("Document import failed: "
                + trResultInfo.MainMessage);
        Guid docRtf01GuidEng = trResultInfo.DocumentGuids[0];
        // Delete file on server, we no longer need it
        getFileManagerService().DeleteFile(fileGuidRtf01);

    #if FULL_TEST

        // Import next document based on its extension with default
        // settings into target lang german only.
        trResultInfo = spService.ImportTranslationDocument(spGuid, fileGuidHtml01,
            new string[] { "ger" }, null);
        if (trResultInfo.ResultStatus == ServerProjectService.ResultStatus.Error)
            throw new Exception("Document import failed: " + trResultInfo.MainMessage);
        Guid docHtmlGuidGer = trResultInfo.DocumentGuids[0];
        // Delete file on server, we no longer need it
        getFileManagerService().DeleteFile(fileGuidHtml01);

        // Import next document based on its extension with default
        // settings into all project target langs.
        trResultInfo = spService.ImportTranslationDocument(spGuid, fileGuidPdf01, null, null);
        if (trResultInfo.ResultStatus == ServerProjectService.ResultStatus.Error)
            throw new Exception("Document import failed: " + trResultInfo.MainMessage);
        Guid docPdf01GuidEng = trResultInfo.DocumentGuids[0];
        Guid docPdf01GuidGer = trResultInfo.DocumentGuids[1];
        // Delete file on server, we no longer need it
        getFileManagerService().DeleteFile(fileGuidPdf01);

        var importXMLSettingsFile = new StreamReader("c:\\_MemoQTest_\\testfiles\\Res_FilterConfig_docx_01.mqres");
        var importXMLSettings = importXMLSettingsFile.ReadToEnd();

        // Import next document based on its extension with default
        // settings into all project target langs.
        trResultInfo = spService.ImportTranslationDocument(spGuid, fileGuidDocx01, null, importXMLSettings);
        if (trResultInfo.ResultStatus == ServerProjectService.ResultStatus.Error)
            throw new Exception("Document import failed: " + trResultInfo.MainMessage);
        Guid docDocx01GuidEng = trResultInfo.DocumentGuids[0];
        Guid docDocx01GuidGer = trResultInfo.DocumentGuids[1];
        // Delete file on server, we no longer need it
        getFileManagerService().DeleteFile(fileGuidDocx01);

        trResultInfo = spService.ImportTranslationDocument(spGuid, fileGuidPptx01, null, null);
        if (trResultInfo.ResultStatus == ServerProjectService.ResultStatus.Error)
            throw new Exception("Document import failed: " + trResultInfo.MainMessage);
        Guid docPptx01GuidEng = trResultInfo.DocumentGuids[0];
        Guid docPptx01GuidGer = trResultInfo.DocumentGuids[1];
        // Delete file on server, we no longer need it
        getFileManagerService().DeleteFile(fileGuidPptx01);

 #endif
        // We could prepare user-doc assigmnent here for all imported
        // docs the same way as for the first one, and save all of
        // them in one step (calling SetProjectTranslationDocumentUserAssignments
        // only once).

        // Pre-translate all docs for all target languages
        // Please note that our TMs are empty, so no mathes are found.
        PretranslateOptions pretranslateOptions = new PretranslateOptions();
        pretranslateOptions.GoodMatchRate = 80;
        pretranslateOptions.OnlyUnambiguousMatches = false;
        pretranslateOptions.PretranslateLookupBehavior =
            PretranslateLookupBehavior.AnyMatch;
        pretranslateOptions.UseMT = false;
        var preTransTaskInfo = spService.StartPretranslateProjectTask(spGuid, null, pretranslateOptions);        
        try
        {
            waitForAsyncTaskToFinishAndGetResult(preTransTaskInfo.TaskId);
        }
        catch (Exception e)
        {
            throw new Exception("Pretranslation failed: " + e.ToString());
        }

        // Statistics
        StatisticsOptions statOptions = new StatisticsOptions();
        statOptions.Algorithm = StatisticsAlgorithm.Trados;
        statOptions.Analysis_Homogenity = true;
        statOptions.Analysis_ProjectTMs = true;
        statOptions.Analyzis_DetailsByTM = true;
        statOptions.IncludeLockedRows = true;
        statOptions.ShowCounts = true;
        statOptions.ShowCounts_IncludeTargetCount = true;
        statOptions.ShowCounts_StatusReport = true;
        statOptions.ShowResultsPerFile = true;
        statOptions.ShowCounts_IncludeWhitespacesInCharCount = false;

        var getStatTaskInfo
            = spService.StartStatisticsOnProjectTask(spGuid, new string[] { "eng", "ger" },
            statOptions, StatisticsResultFormat.CSV_Trados);
        try
        {
            var statResult = waitForAsyncTaskToFinishAndGetResult(getStatTaskInfo.TaskId) as StatisticsTaskResult;
        }
        catch(Exception e)
        {
            throw new Exception("Statistics failed: " + e.ToString());
        }
        // In statResult.ResultsForTargetLangs[0].ResultData we have the result
        // for the first target language requested.
        // In statResult.ResultsForTargetLangs[1].ResultData we have the result
        // for the second target language requested.

        // Export document A
        TranslationDocExportResultInfo resultInfo;
        string exportedFileName;
        string exportDir = "c:\\_MemoQTest_\\out";

        // Export and download the first imported document, english version.
        resultInfo = spService.ExportTranslationDocument(spGuid,
            docAGuidEng);
        if (resultInfo.ResultStatus == ServerProjectService.ResultStatus.Error)
            throw new Exception("There was an error during the export," +
            " with message: " + resultInfo.MainMessage);
        // Download the result of the export of document A
        downloadSampleDoc(resultInfo.FileGuid, out exportedFileName, exportDir);
        // Delete file on server, we no longer need it
        getFileManagerService().DeleteFile(resultInfo.FileGuid);

        // Export and download the first imported document, german version.
        resultInfo = spService.ExportTranslationDocument(spGuid,
            docAGuidGer);
        if (resultInfo.ResultStatus == ServerProjectService.ResultStatus.Error)
            throw new Exception("There was an error during the export," +
            " with message: " + resultInfo.MainMessage);
        // Download the result of the export of document A
        downloadSampleDoc(resultInfo.FileGuid, out exportedFileName, exportDir);
        // Delete file on server, we no longer need it
        getFileManagerService().DeleteFile(resultInfo.FileGuid);

    #if FULL_TEST
        // Export and download the first imported document, german version.
        exportTranslationDocument(spService, spGuid, docRtf01GuidEng, exportDir);
        exportTranslationDocument(spService, spGuid, docHtmlGuidGer, exportDir);
        exportTranslationDocument(spService, spGuid, docPdf01GuidEng, exportDir);
        exportTranslationDocument(spService, spGuid, docPdf01GuidGer, exportDir);
        exportTranslationDocument(spService, spGuid, docDocx01GuidEng, exportDir);
        exportTranslationDocument(spService, spGuid, docDocx01GuidGer, exportDir);
        exportTranslationDocument(spService, spGuid, docPptx01GuidEng, exportDir);
        exportTranslationDocument(spService, spGuid, docPptx01GuidEng, exportDir);
    #endif

        // -------------- Create DesktopDocs project, set and query document
        //                workflow status
        ServerProjectDesktopDocsCreateInfo spDDCreateInfo =
            new ServerProjectDesktopDocsCreateInfo()
        {
            Name = "DemoSP_" + Guid.NewGuid().ToString(),
            SourceLanguageCode = "hun",
            TargetLanguageCodes = new string[] { "eng" },
            Subject = "###unittest###",
            Deadline = DateTime.Now + TimeSpan.FromDays( 1 ),
            CreatorUser = TestUsers.TestUserAdminGuid
        };
        spGuid = spService.CreateProject( spDDCreateInfo );

        // Upload a file which is to be imported next
        fileGuidTxt01 = uploadSampleFile( pathOfTxt01 );
        // Import first file based on its extension with default
        // settings into all project target langs.
        trResultInfo = spService.ImportTranslationDocument( spGuid, fileGuidTxt01,
            null, null );
        // Delete file on server, we no longer need it
        getFileManagerService().DeleteFile( fileGuidTxt01 );
        if( trResultInfo.ResultStatus == ServerProjectService.ResultStatus.Success )
        {
            // Check the workflow status of the document (we know that there is
            // one document in the project.
            WorkflowStatus workflowStat =
                spService.ListProjectTranslationDocuments( spGuid )[0]
                .WorkflowStatus;
            // Change the workflow status
            spService.SetDocumentWorkflowStatus( spGuid,
                new ServerProjectTranslationDocumentWorkflowStatusChange[]
                {
                    // One instance for every document to change
                    new ServerProjectTranslationDocumentWorkflowStatusChange()
                    {
                        DocumentGuid = trResultInfo.DocumentGuids[0],
                        WorkflowStatus = WorkflowStatus.Review1InProgress
                    }
                } );
        }
        return spName;
    }

    internal string StatisticsAsyncTest()
    {
        string spName;
        TranslationDocImportResultInfo trResultInfo;
        // Get server project service object.
        IServerProjectService spService = getServerProjectService();

        // Create server project, the guid of the newly created project
        // is returned.
        Guid spGuid = createSampleProject(out spName, "hun",
            new string[] { "eng", "ger" }, false);

        string pathOfFile = @"c:\_MemoQTest_\testfiles\#I_htm#.htm";
        Guid fileGuid = uploadSampleFile(pathOfFile);
        trResultInfo = spService.ImportTranslationDocument(spGuid, fileGuid,
            null, null);
        if (trResultInfo.ResultStatus == ServerProjectService.ResultStatus.Error)
            throw new Exception("Document import failed: " +
                trResultInfo.MainMessage);

        // Statistics
        StatisticsOptions statOptions = new StatisticsOptions();
        statOptions.Algorithm = StatisticsAlgorithm.MemoQ;
        statOptions.Analysis_Homogenity = true;
        statOptions.Analysis_ProjectTMs = true;
        statOptions.Analyzis_DetailsByTM = true;
        statOptions.IncludeLockedRows = true;
        statOptions.ShowCounts = true;
        statOptions.ShowCounts_IncludeTargetCount = true;
        statOptions.ShowCounts_StatusReport = true;
        statOptions.ShowResultsPerFile = true;
        statOptions.ShowCounts_IncludeWhitespacesInCharCount = true;

        string outDir = "c:\\_MemoQTest_\\out";
        string statFilePath = Path.Combine(outDir, "statresult - " + Guid.NewGuid() + ".html");
        var statisticsTaskInfo = spService.StartStatisticsOnProjectTask(spGuid, null, statOptions, StatisticsResultFormat.Html);
        try
        {
            var statisticsResult = waitForAsyncTaskToFinishAndGetResult(statisticsTaskInfo.TaskId) as StatisticsTaskResult;
            using (MemoryStream stream = new MemoryStream(statisticsResult.ResultsForTargetLangs[0].ResultData))
            using (StreamReader sr = new StreamReader(stream, Encoding.Unicode))
            using (StreamWriter sw = new StreamWriter(statFilePath))
            {
                string s = sr.ReadToEnd();
                sw.Write(s);
            }
        }
        catch (Exception e)
        {
            throw new Exception("Statistics failed: " + e.ToString());
        }
        return statFilePath;
    }

    struct TestUsers
    {
        // Built in administrator
        public static readonly Guid TestUserAdminGuid =
            new Guid("00000000-0000-0000-0001-000000000001");
        // Built in administrator
        public static readonly Guid TestUserGuestGuid =
            new Guid("00000000-0000-0000-0001-000000000002");
    };

    #region Getting service objects

    /// <summary>
    /// Gets a ITMService service object. This is platform and technology
    /// specific.
    /// </summary>
    /// <returns></returns>
    ITMService getTMService()
    {
        ChannelFactory<ITMService> channelFactory =
            new ChannelFactory<ITMService>("TMService");
        return channelFactory.CreateChannel();
    }

    /// <summary>
    /// Gets a ITBService service object. This is platform and technology
    /// specific.
    /// </summary>
    /// <returns></returns>
    ITBService getTBService()
    {
        ChannelFactory<ITBService> channelFactory =
            new ChannelFactory<ITBService>("TBService");
        return channelFactory.CreateChannel();
    }

    /// <summary>
    /// Gets a ISecurityService service object. This is platform and technology
    /// specific.
    /// </summary>
    /// <returns></returns>
    ISecurityService getSecurityService()
    {
        ChannelFactory<ISecurityService> channelFactory =
            new ChannelFactory<ISecurityService>("SecurityService");
        return channelFactory.CreateChannel();
    }

    /// <summary>
    /// Gets a IServerProjectService service object. This is platform and
    /// technology specific.
    /// </summary>
    /// <returns></returns>
    IServerProjectService getServerProjectService()
    {
        ChannelFactory<IServerProjectService> channelFactory =
            new ChannelFactory<IServerProjectService>("ServerProjectService");
        return channelFactory.CreateChannel();
    }

    /// <summary>
    /// Gets an IResourceService service object. This is platform and
    /// technology specific.
    /// </summary>
    /// <returns></returns>
    IResourceService getResourceService()
    {
        ChannelFactory<IResourceService> channelFactory =
            new ChannelFactory<IResourceService>("ResourceService");
        return channelFactory.CreateChannel();
    }

    /// <summary>
    /// Gets a IFileManagerService service object. This is platform and
    /// technology specific.
    /// </summary>
    /// <returns></returns>
    IFileManagerService getFileManagerService()
    {
        ChannelFactory<IFileManagerService> channelFactory =
            new ChannelFactory<IFileManagerService>("FileManagerService");
        return channelFactory.CreateChannel();
    }

    /// <summary>
    /// Gets a ITasksService service object. This is platform and
    /// technology specific.
    /// </summary>
    /// <returns></returns>
    ITasksService getTasksService()
    {
        ChannelFactory<ITasksService> channelFactory =
        new ChannelFactory<ITasksService>("TasksService");
        return channelFactory.CreateChannel();
    }

        #endregion

        #region Helper methods

        /// <summary>
        /// Creates and publishes a sample translation memory.
        /// </summary>
        /// <returns>The guid of the newly created TM.</returns>
        Guid createAndPublishSampleTM(string sourceLangCode,
        string targetLangCode)
    {
        MemoQServicesClient.TMService.TMInfo tmInfo;
        tmInfo = new MemoQServicesClient.TMService.TMInfo(
            Guid.NewGuid(), // This guid is ignored.
            "TestTM1_Name_" + Guid.NewGuid().ToString(),
            "##UnitTest##",
            false, false, false, false,
            sourceLangCode, targetLangCode);
        ITMService tmService = getTMService();
        // CreateAndPublish returns the guid of the new TM
        return tmService.CreateAndPublish(tmInfo);
    }

    Guid createAndPublishSampleTB()
    {
        return createAndPublishSampleTB(new string[] { "hun", "eng", "ger" });
    }

    /// <summary>
    /// Creates and publishes a sample translation memory.
    /// </summary>
    /// <returns>The guid of the newly created TB.</returns>
    Guid createAndPublishSampleTB(string[] langs)
    {
        MemoQServicesClient.TBService.TBInfo tbInfo;
        tbInfo = new MemoQServicesClient.TBService.TBInfo(
            Guid.NewGuid(), // This guid is ignored.
            "TestTB1_Name" + Guid.NewGuid().ToString(),
            "##UnitTest##", false, langs);
        ITBService tbService = getTBService();
        // CreateAndPublish returns the guid of the new TB
        return tbService.CreateAndPublish(tbInfo);
    }

    /// <summary>
    /// Creates a sample server project.
    /// </summary>
    /// <param name="name">The name of the project.</param>
    /// <param name="targetLangCodes">The target language codes of the
    /// project.</param>
    /// <returns>The guid of the newly created project.</returns>
    Guid createSampleProject(out string name, string sourceLangCode,
        string[] targetLangCodes, bool isVersioned)
    {
        var spCreateInfo = new ServerProjectDesktopDocsCreateInfo()
        {
            // Resulted in PathTooLongException, cut down a little bit
            Name = "DemoSP_" + Guid.NewGuid().ToString(),
            //Name = "SP" + DateTime.Now.ToString("MMdd.HHmmss.fff"),
            SourceLanguageCode = sourceLangCode,
            TargetLanguageCodes = targetLangCodes,
            Subject = "###unittest###",
            Deadline = DateTime.Now + TimeSpan.FromDays(1),
            CreatorUser = TestUsers.TestUserAdminGuid,
            RecordVersionHistory = isVersioned
        };

        IServerProjectService serverProjectService = getServerProjectService();
        name = spCreateInfo.Name;
        return serverProjectService.CreateProject(spCreateInfo);
    }

    /// <summary>
    /// Uploads the sample file.
    /// </summary>
    /// <param name="filePath">The file path.</param>
    /// <returns>The guid of the uploaded file.</returns>
    Guid uploadSampleFile(string filePath)
    {
        FileStream fileStream = null;
        Guid fileGuid = Guid.Empty;
        int chunkSize = 1000000;
        byte[] chunkBytes = new byte[chunkSize];
        byte[] dataToUpload;
        int bytesRead;

        IFileManagerService fmService = getFileManagerService();

        try
        {
            // Open souce file stream
            fileStream = File.OpenRead(filePath);

            // Begin chunked upload
            fileGuid = fmService.BeginChunkedFileUpload(filePath, false);

            // Upload chunks
            while ((bytesRead = fileStream.Read(chunkBytes, 0, chunkSize)) != 0)
            {
                if (bytesRead == chunkSize)
                    dataToUpload = chunkBytes;
                else
                {
                    // If we are at the end, we want to upload only
                    // the bytes read from the stream.
                    dataToUpload = new byte[bytesRead];
                    Array.Copy(chunkBytes, dataToUpload, bytesRead);
                }

                fmService.AddNextFileChunk(fileGuid, dataToUpload);
            }

            return fileGuid;
        }
        finally
        {
             if (fileStream != null)
                  fileStream.Close();

            // End chunked upload
            if (fileGuid != Guid.Empty)
                fmService.EndChunkedFileUpload(fileGuid);
        }
    }

    /// <summary>
    /// Downloads the sample document.
    /// </summary>
    /// <param name="fileGuid">The file GUID.</param>
    /// <param name="fileName">Name of the file.</param>
    /// <param name="targetGir">The target gir.</param>
    void downloadSampleDoc(Guid fileGuid, out string fileName, string targetGir)
    {
        FileStream fileStream = null;
        Guid sessionGuid = Guid.Empty;
        int chunkSize = 1000000;
        byte[] chunkBytes = new byte[chunkSize];
        int fileSize;
        int fileBytesLeft;

        IFileManagerService fmService = getFileManagerService();

        try
        {
            sessionGuid = fmService.BeginChunkedFileDownload(out fileName, out fileSize,
                fileGuid, false);
            fileStream = new FileStream(Path.Combine(targetGir, fileName),
                FileMode.Create);
            fileBytesLeft = fileSize;

            while (fileBytesLeft > 0)
            {
                chunkBytes = fmService.GetNextFileChunk(sessionGuid, chunkSize);
                fileStream.Write(chunkBytes, 0, chunkBytes.Length);
                fileBytesLeft -= chunkBytes.Length;
            }
        }
        finally
        {
            if (fileStream != null)
                  fileStream.Close();

            if (sessionGuid != Guid.Empty)
                fmService.EndChunkedFileDownload(sessionGuid);
        }
    }

    private void exportTranslationDocument(IServerProjectService spService,
        Guid spGuid, Guid docGuid, string exportDir)
    {
        TranslationDocExportResultInfo resultInfo;
        string exportedFileName;
        resultInfo = spService.ExportTranslationDocument(spGuid,
                                                         docGuid);
        if (resultInfo.ResultStatus == ServerProjectService.ResultStatus.Error)
            throw new Exception("There was an error during the export," +
                                " with message: " + resultInfo.MainMessage);
        // Download the result of the export of document A
        downloadSampleDoc(resultInfo.FileGuid, out exportedFileName, exportDir);
        // Delete file on server, we no longer need it
        getFileManagerService().DeleteFile(resultInfo.FileGuid);
    }

    private TaskResult waitForAsyncTaskToFinishAndGetResult(Guid TaskId)
    {
        ITasksService taskService = getTasksService();
        var taskStatus = taskService.GetTaskStatus(TaskId).Status;
        int i = 1;
        while (taskStatus != TasksService.TaskStatus.Cancelled && taskStatus != TasksService.TaskStatus.Completed
            && taskStatus != TasksService.TaskStatus.Failed && taskStatus != TasksService.TaskStatus.InvalidTask)
        {
            if (i < 16)
                i = 2 * i;
            Thread.Sleep(i * 1000);
            taskStatus = taskService.GetTaskStatus(TaskId).Status;
        }

        if (taskService.GetTaskStatus(TaskId).Status != TasksService.TaskStatus.Completed)
            throw new Exception($"Task is {taskService.GetTaskStatus(TaskId).Status.ToString()}.");
        return taskService.GetTaskResult(TaskId);
    }

        #endregion

        #region Develeper temp tests, not part of sample code

    public static String byteArrayToHexString(byte[] b)
    {
        StringBuilder sb = new StringBuilder(b.Length * 2);
        for (int i = 0; i < b.Length; i++)
        {
            int v = b[i] & 0xff;
            sb.Append(v.ToString("X2"));
        }
        return sb.ToString().ToUpper();
    }

        #endregion
    }
}
